<> </> 是什么?
Fragment组件
在 React 中,<></> 是 Fragment 组件的语法糖,用于在不引入额外DOM节点的情况下,包裹一组子元素。
它的主要作用就是让你可以在需要单一父节点的地方,返回多个元素,但不会在最终的 DOM 解构中生成多余的包裹节点。
常见写法有两种:
// 语法糖写法
<>
<ChildA />
<ChildB />
</>
等价于:
// 完整写法
<React.Fragment>
<ChildA />
<ChildB />
</React.Fragment>
解决了什么问题?
JSX 语法要求:组件必须有且只有一个根节点。以往我们会用
<div>包裹所有内容,导致 DOM 结构层级变深,出现“多余的 div”。Fragment 的作用:允许返回多个子元素,不会在 DOM 中生成额外节点,避免“为了 div 而 div”的问题,保持了DOM结构的间接性。
用法
比如你在组件中这么去写
function Post() {
return (
<>
<PostTitle />
<PostBody />
</>
);
}
在最终渲染的时候,<PostTitle /> 和 <PostBody /> 会作为兄弟节点出现在 DOM 中,没有额外的包裹元素。
列表渲染时的key
在循环渲染时,如果需要为 Fragment 添加 key,必须使用 <React.Fragment key={...}> 的完整写法,语法糖 <>...</> 不支持 key 属性。
import { Fragment } from 'react';
const posts = [
{ id: 1, title: 'An update', body: "It's been a while..." },
{ id: 2, title: 'My new blog', body: 'I am starting a new blog!' }
];
export default function Blog() {
return posts.map(post =>
<Fragment key={post.id}>
<PostTitle title={post.title} />
<PostBody body={post.body} />
</Fragment>
);
}
Fragment是否会影响真实DOM?
Fragment只会影响 React 虚拟 DOM 层,不会在真实 DOM 产生任何标签
React 虚拟 DOM 层
React 在渲染组件时,首先会在内存中构建一棵“虚拟 DOM 树”(Virtual DOM Tree),这是一种用 JavaScript 对象描述的 UI 结构。Fragment 只是在这棵虚拟 DOM 树中起到“分组”作用,让多个子节点可以作为一个整体被管理。
不会在真实 DOM 产生标签
当 React 最终将虚拟 DOM 渲染为真实 DOM 时,Fragment 本身不会被转化为任何 HTML 标签。也就是说,Fragment 只是一个“容器”,它的子元素会直接作为父节点的子节点插入到真实 DOM 中,不会有 <div>、<span> 或 <fragment> 这样的标签出现。
比如:
function Demo() {
return (
<>
<h1>标题</h1>
<p>内容</p>
</>
);
}
最终在浏览器的DOM结构中,只会看到:
<h1>标题</h1>
<p>内容</p>
我们会发现的就是:没有任何包裹这两个元素的额外标签
如果给<></>中加入div,即<div></div>,此时DOM结构会多出一个<div>:
<div>
<h1>标题</h1>
<p>内容</p>
</div>
这与上面多出的一个<div>会让DOM树多了一层不需要的节点,需要再迭代一层,使得DOM解析性能下降。所以,jsx最外层一定要有唯一的父元素,不要为了div而div。
Fragment的切换是否会重置子组件的状态?
Fragment 的切换不会重置子组件的状态(除非嵌套多层 Fragment 时结构发生变化)
为什么?
- 组件状态的保持
React 通过虚拟 DOM 的“节点标识”来判断组件是否是“同一个”,从而决定是否保留其内部状态(如 useState、useRef、表单输入等)。
- Fragment 的切换
当你在组件返回值中,从
<><Child /></>切换到[<Child />]或<Child />,React 会认为这些结构在同一层级下,<Child />还是原来的那个组件,所以不会重置其状态。
- 只有结构发生根本变化时才会重置
只有当你嵌套多层 Fragment,并且结构发生变化(比如从
<><><Child /></></>切换到<Child />),React 才会认为这是不同的节点,从而重置子组件的状态。
比如:
function App() {
const [useFragment, setUseFragment] = useState(true);
return (
<div>
<button onClick={() => setUseFragment(f => !f)}>切换</button>
{useFragment
? (
<>
<Child />
</>
)
: (
<Child />
)
}
</div>
);
}
function Child() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
无论怎么切换,Child 组件的计数状态都不会丢失,因为 React 认为它们是同一个节点。
但如果你多嵌套了一层Fragment:
{useFragment
? (
<><><Child /></></>
)
: (
<Child />
)
}
此时切换时,Child 的状态会被重置,因为 React 认为节点结构发生了变化。
总结
Fragment 是 React 组件开发中非常实用的工具,能让你的组件结构更简洁、DOM 更干净,尤其在避免“多余的 div”时非常有用。Fragment 只存在于 React 虚拟 DOM 层,真实 DOM 不会有任何对应标签。Fragment 的切换不会导致子组件状态丢失,除非嵌套层级发生变化,导致 React 识别为不同节点。