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,可以为其指定
key
jsximport { 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 节点数量) |