React 中的 <></> Fragment:是什么及其原理详解
在 React 开发中,经常需要返回多个组件或元素,而 React 的 JSX 语法要求每个组件必须有一个根节点 。为了解决这个问题,React 提供了一个**Fragment(片段)**来解决这个问题。Fragment 的简写形式就是 <></>,它允许我们包裹多个子元素而不向 DOM 中添加额外的节点。
一、Fragment 是什么?
Fragment 是 React 提供的一个特殊的组件,用于包裹多个子元素,但它不会在最终的 DOM 中渲染出任何实际的节点。换句话说,它只是一个逻辑上的包裹结构,而不是一个真实的 DOM 元素。
在 JSX 中,Fragment 的写法有两种:
- 显式写法:
<React.Fragment>...</React.Fragment> - 简写形式:
<></> 
例如:
            
            
              jsx
              
              
            
          
          return (
  <>
    <h1>标题</h1>
    <p>内容</p>
  </>
);
        等价于:
            
            
              jsx
              
              
            
          
          return (
  <React.Fragment>
    <h1>标题</h1>
    <p>内容</p>
  </React.Fragment>
);
        二、为什么需要 Fragment?
在 React 中,每个组件的 render 函数或函数组件的返回值必须是一个单一的 React 元素。如果你尝试返回多个并列的元素,会报错
            
            
              jsx
              
              
            
          
          // 错误示例
return (
  <h1>标题</h1>
  <p>内容</p>
);
        
            
            
              jsx
              
              
            
          
          return (
	<div id='container'>
		<h1>标题</h1>
  	<p>内容</p>
	</div>
);
        
若使用 <div> 包裹多个元素会增加 DOM 树的深度,导致:
- 
页面结构变得复杂
 - 
可能影响子元素的定位、布局
使用flex、Grid布局时,父容器的直接子元素是布局的关键,如果插入了一个div,则会形成一个新的布局容器,从而打乱原有的布局。
 - 
影响 CSS 选择器(如
:nth-child)的行为 
而使用 Fragment 可以优雅地包裹多个元素,而不会引入多余的 DOM 节点。这对于保持 DOM 结构的整洁非常重要,尤其是在使用 CSS Flex、Grid 布局时,避免因多余的容器节点影响布局。
            
            
              jsx
              
              
            
          
          return (
	<>
		<h1>标题</h1>
  		<p>内容</p>
	</>
);
        
三、Fragment 的特点
- 
不渲染 DOM 节点 , Fragment 只是一个逻辑结构,不会生成真实的 DOM
 - 
支持 key 属性 ,如果你需要渲染一个列表中的 Fragment,可以为其指定
keyjsximport { Fragment } from "react"; import "./App.css"; function App() { const lists = [ { id: 1, container: "内容1" }, { id: 2, container: "内容2" }, { id: 3, container: "内容3" }, ]; return lists.map((item) => ( <Fragment key={item.id}> <h2>{item.container}</h2> </Fragment> )); } 

- 
不支持 ref 和 style ,Fragment 不能绑定
ref或设置style,因为它不是一个真正的 DOM 元素 - 
可嵌套使用 Fragment 可以嵌套在其他 Fragment 或组件中
 
四、使用原生js实现与Fragment相同的效果
使用document.createDocumentFragment() 可以创建一个轻量级的文档片段(document fragment)。这个文档片段不属于主 DOM 树,但可以像标准 DOM 节点一样被操作。也就是说在最后的DOM树中不会有该节点的存在,其效果和Fragment一样。
以下的代码逻辑是使用document.createDocumentFragment()创建一个document fragment,然后一次性将多个节点添加到这个片段中,最后统一插入到DOM树中。document fragment 在这里充当一个临时的节点。
            
            
              html
              
              
            
          
              <div id="container"></div>
    <script>
      const lists = [
        {
          id: 1,
          title: '111',
          content: '111111',
        },
        {
          id: 2,
          title: '222',
          content: '222222',
        },
      ]
		
      // 创建document fragment 文档片段
      const fragment = document.createDocumentFragment()
      const container = document.getElementById("container");
		
        // 向文档片段插入节点
      lists.forEach((item) => {
        const div = document.createElement('div')
        const title = document.createElement('h2')
        const content = document.createElement('p')
        title.textContent = item.title
        content.textContent = item.content
        div.appendChild(title)
        div.appendChild(content)
        
        fragment.appendChild(div)
      });
      
      container.appendChild(fragment)
    </script>
        效果如下

五、Fragment 的使用场景
- 
返回多个并列元素
jsxfunction UserInfo() { return ( <> <h2>用户名:Tom</h2> <p>年龄:25</p> </> ); } - 
条件渲染包裹多个元素
jsxfunction Greeting({ isLoggedIn }) { return ( <> {isLoggedIn ? <p>欢迎回来!</p> : <p>请登录</p>} </> ); } - 
在列表中使用带有 key 的 Fragment
jsxfunction TodoList({ items }) { return ( <ul> {items.map(item => ( <React.Fragment key={item.id}> <li>{item.text}</li> <hr /> </React.Fragment> ))} </ul> ); } 
六、Fragment 与 <div> 的区别
| 对比项 | Fragment | <div> | 
|---|---|---|
| 是否生成 DOM 节点 | 否 | 是 | 
| 是否影响布局 | 否 | 是(可能破坏 CSS 布局) | 
| 是否影响语义 | 否 | 是(可能影响语义结构) | 
| 是否影响性能 | 否 | 是(增加 DOM 节点数量) |