🧩 React Fragment 的底层原理:为什么它能“包裹却不包裹”?

🧩 React Fragment 的底层原理:为什么它能"包裹却不包裹"?

你是否好奇,为什么 <Fragment> 可以像个"幽灵容器"一样不渲染任何 HTML,却又能同时包裹多个元素?本篇文章将带你一探 React Fragment 的底层奥秘!


🌟 前言:为什么需要 Fragment?

在早期的 React 中,我们写组件时遇到一个经典问题:

javascript 复制代码
// ❌ 错误:JSX 必须有一个根节点
return (
  <h1>标题</h1>
  <p>段落</p>
);

JSX 只能返回一个根节点,为了规避报错,我们常常不得不这样写:

javascript 复制代码
// ✅ 有效但冗余的 div
return (
  <div>
    <h1>标题</h1>
    <p>段落</p>
  </div>
);

但是,这种多余的 <div> 会污染 DOM 结构,不利于样式控制和性能优化。为此,React 引入了 Fragment ------一个"看得见但摸不着"的神秘容器。


👻 什么是 Fragment?

<Fragment> 是 React 提供的一个特殊组件,它 允许你返回多个子元素而不额外包裹 DOM 元素

javascript 复制代码
import React, { Fragment } from 'react';

return (
  <Fragment>
    <h1>标题</h1>
    <p>段落</p>
  </Fragment>
);

或者使用更简洁的写法(空标签):

xml 复制代码
<>
  <h1>标题</h1>
  <p>段落</p>
</>

渲染结果👇

css 复制代码
<h1>标题</h1>
<p>段落</p>

没有任何多余的 div 或 span!


🔍 Fragment 背后的工作机制

1. 虚拟 DOM 层:Fragment 是一个"占位节点"

在 React 的虚拟 DOM 中,Fragment 是一种 特殊类型的 Fiber 节点 ,它的 type 并不是 'div',而是一个内部标识:

typescript 复制代码
type FiberNode = {
  type: string | function | Symbol;
  tag: number;
  ...
}

当使用 <Fragment> 时,React 会生成一个 REACT_FRAGMENT_TYPE 类型的 Fiber:

ini 复制代码
const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');

这一类型告诉 React:"这是个 Fragment,不要生成真实 DOM 元素。"


2. 渲染阶段:跳过 Fragment 本身

ReactDOM 的渲染过程中,Fragment Fiber 的作用仅仅是用来 组织它的子节点

  • Fragment 自己不会被转化为 DOM 节点。
  • React 会跳过它,直接递归处理它的子节点。

底层逻辑类似下面伪代码:

scss 复制代码
if (fiber.type === REACT_FRAGMENT_TYPE) {
  // 不创建 DOM 元素,跳过自己
  return renderChildren(fiber.children);
}

3. 为什么可以写成 <> </>

这是 React 对 Fragment 的语法糖,等价于:

xml 复制代码
<>内容</> === <React.Fragment>内容</React.Fragment>

这在 Babel 编译阶段就会转换成:

csharp 复制代码
React.createElement(React.Fragment, null, ...children);

📦 Fragment 的高级玩法

1. keyed Fragment(带 key 的 Fragment)

当你在列表中返回 Fragment 时,可以也应该加上 key

javascript 复制代码
data.map(item => (
  <React.Fragment key={item.id}>
    <dt>{item.term}</dt>
    <dd>{item.description}</dd>
  </React.Fragment>
));

带 key 的 Fragment 会被 React 正常地用于 Diff 过程,性能更优,避免不必要的重渲染


2. Fragment vs div:性能对比

对比项 Fragment div
是否生成 DOM ❌ 不生成 ✅ 会生成
影响样式 ✅ 无影响 ❌ 可能影响布局
性能 ✅ 更快(跳过生成) ⛔️ 有额外创建成本
使用场景 ✅ 结构包装、列表 ⛔️ 用于样式布局再考虑

🧠 总结一下

问题 解答
Fragment 会不会生成 DOM? 不会。
Fragment 是怎么实现的? 通过虚拟 DOM 中特殊的 REACT_FRAGMENT_TYPE 类型。
它存在的意义? 不污染 DOM,又能返回多个元素。
空标签 <> </> 是什么? React.Fragment 的语法糖。
可以加 key 吗? 可以,尤其在列表中推荐加。

✨ 结尾彩蛋:Fragment 在源码中的位置

你可以在 React 的源码 ReactElement.js 里找到这段:

ini 复制代码
export const REACT_FRAGMENT_TYPE = Symbol.for('react.fragment');

而在调和阶段(reconciler)中,也有对 REACT_FRAGMENT_TYPE 的判断分支,用于正确跳过 DOM 创建,专心处理子节点。

相关推荐
小码哥_常1 小时前
安卓黑科技:实现多平台商品详情页一键跳转APP
前端
killerbasd1 小时前
还是迷茫 5.3
前端·react.js·前端框架
不会敲代码12 小时前
TCP/IP 与前端性能:从数据包到首次渲染的底层逻辑
前端·tcp/ip
kyriewen2 小时前
奥特曼借GPT-5.5干杯,而你的Copilot正按Token收钱
前端·github·openai
AC赳赳老秦2 小时前
投标合规提效:用 OpenClaw 实现标书 / 合同自动审核、关键词校验、格式优化,降低废标风险
开发语言·前端·python·eclipse·emacs·deepseek·openclaw
kyriewen2 小时前
代码写成一锅粥?3个设计模式让你的项目“起死回生”
前端·javascript·设计模式
千寻girling3 小时前
《 Git 详细教程 》
前端·后端·面试
之歆4 小时前
DAY08_CSS浮动与行内块布局实战指南(下)
前端·css
yqcoder4 小时前
CSS Position 全解析:5 种定位模式详解
前端·css
Rhi6375 小时前
从零搭建项目:React 19 + Vite 8 + Tailwind CSS v4 实战配置
前端