你不会还不知道JSX中<></>是什么吧?

71 阅读4分钟

<> </> 是什么?

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 识别为不同节点。

参考资料

React 官方文档 Fragment