一、Fragment 的核心概念
Fragment 是 React 提供的一个特殊组件,它用于在不引入额外 DOM 节点的前提下,渲染多个子元素。 它不仅解决了 JSX 要求必须有一个单一父元素的限制,同时避免了因额外容器元素导致的性能损耗和 DOM 结构冗余。
1. 为什么需要 Fragment?
在 React 的 JSX 中规定,每个组件的返回值必须是一个单一的根元素。例如:
jsx
function App() {
return (
<div>
<h1>标题</h1>
<p>内容</p>
</div>
);
}
在这里,你必须使用 <div>
来包裹两个子元素,然而,你会发现,如果这样做,那么DOM 中就会多出一个无意义的 <div>
,非常影响样式和性能。
而使用 Fragment 刚好可以完美解决这个问题。
二、Fragment 的核心特性
1. 避免冗余 DOM 结构
Fragment 允许直接返回多个元素,而无需添加额外的容器节点。
示例:使用 Fragment 替代 <div>
jsx
function App() {
return (
<Fragment>
<h1>标题</h1>
<p>内容</p>
</Fragment>
);
}
对比:
- 使用
<div>
:

- 使用 Fragment:

分析: 传统的 <div>
会生成额外的 DOM 节点,导致不必要的层级嵌套,Fragment 通过虚拟 DOM 机制,直接将子元素插入目标位置,避免生成真实 DOM 节点。
2. 提升性能
Fragment 的底层就是基于 **文档碎片(DocumentFragment)**的,它可以通过批量操作 DOM 来减少重排重绘的次数。
示例:
html
<script>
const fragment = document.createDocumentFragment();
const title = document.createElement('h1');
const content = document.createElement('p');
fragment.appendChild(title);
fragment.appendChild(content);
document.body.appendChild(fragment); // 一次性插入,减少重排
</script>
分析:
-
如果按照传统方法,那么逐个插入 DOM 元素(如使用
appendChild(wrapper)
),一定会导致页面的多次重排重绘,这样非常消耗性能。 -
而通过上面的代码,我们先将所有元素添加到文档碎片中,再一次性插入 DOM,这样减少浏览器的渲染压力,比如:
-
document.createDocumentFragment()
:这里我们创建了一个虚拟 DOM 容器DocumentFragment
,用来临时存储多个 DOM 元素。 -
而且,这个节点非常特殊,它不会被插入到实际的 DOM 树中,并且可以像普通 DOM 节点一样操作(如添加子元素)。
-
因此,它也不会触发浏览器的重排(Reflow)和重绘(Repaint) 操作。
-
最终,在将其他子标签添加到这个容器后,它就将
DocumentFragment
中的所有子元素一次性插入到<body>
中。
-
3. 支持 key 属性
在列表渲染中,Fragment 必须配合 key
属性使用,以避免 React 警告。
示例:列表中的Fragment
jsx
const items = [
{ id: 1, title: '标题1', content: '内容1' },
{ id: 2, title: '标题2', content: '内容2' }
];
function Demo({ items }) {
return items.map(item => (
<Fragment key={item.id}>
<h1>{item.title}</h1>
<p>{item.content}</p>
</Fragment>
));
}
分析:
React 要求列表中的每个元素必须有唯一 key
,否则会抛出警告,因为 Fragment 添加 key
之后,才能确保 React 能正确识别每个子元素组,并且key
只能用在 Fragment 的顶层,不能嵌套使用。
四、Fragment 的实现原理
1. 语法糖 vs 显式导入
-
语法糖 :
<Fragment></Fragment>
可以简写为<></>
,这也是我们创建一个React项目后App.jsx中常见的标签。 -
显式导入 :如果需要像上面讲得一样使用
Fragment
标签,那么你要先从react
中导入Fragment
组件才能使用。
jsx
import { Fragment } from 'react';
到这里你可能会想,既然能简写成<></>
,我为什么还要使用更复杂的Fragment
。
那么这里就要看使用场景了。
<></>
更简洁,适合简单的场景,而显式导入 Fragment
则可以指定 key
或添加注释,所以我们一般优先使用 <></>
,只有在需要显式配置时才用 Fragment
。
2. 虚拟 DOM 的优化
上面说过,React 的虚拟 DOM 会将 Fragment 的子元素直接插入到真实 DOM 中,不会创建实际的节点。例如:
jsx
<>
<h1>标题</h1>
<p>内容</p>
</>
实际上,它会被转换为:
javascript
React.createElement(React.Fragment, null,
React.createElement('h1', null, '标题'),
React.createElement('p', null, '内容')
);
分析: React 会通过虚拟 DOM 对比差异,把 Fragment 的子元素直接插入目标位置,来避免生成额外的 DOM 节点,减少内存占用和渲染时间。
五、总结
1. Fragment 的核心优势
- 简化 DOM 结构:避免不必要的容器节点。
- 提升性能:减少 DOM 操作次数,优化重排重绘。
- 灵活渲染:支持多元素返回和条件渲染。
2. 最佳实践
- 优先使用 Fragment:在需要返回多个元素时,优先选择 Fragment。
- 列表渲染中使用 key:确保每个 Fragment 有唯一的标识。
- 避免嵌套 Fragment:过度嵌套可能导致可读性下降。