React 里的 <></> 是啥?为啥说它是优化 DOM 的小能手?

在写 React 组件时,你是不是也遇到过这种情况:JSX 代码明明逻辑清晰,却非得在外层套一个多余的div?否则就会报错 "JSX 相邻的元素必须被包裹在一个闭合标签中"。而<></>的出现,就像给这个问题开了一剂特效药。今天我们就来聊聊这个神秘的符号,它到底是什么?

先看个痛点:为什么非得套个 div?

在 React 中,JSX 语法有个严格规定:最外层必须有唯一的父元素。比如下面这段代码会报错:

jsx 复制代码
function UserInfo() {
  return (
    <h1>用户信息</h1>
    <p>姓名:张三</p>
    <p>年龄:25</p>
  );
}

JSX 最终会被编译成React.createElement函数调用,而多个相邻元素会被解析成函数的多个参数,这在 JavaScript 中是不允许的。因此,我们必须给它们套一个父元素,最常用的就是div

但这种写法会带来两个问题:

  1. 多余的 DOM 层级 :这个div没有任何实际意义,只是为了满足语法要求,却给 DOM 树增加了不必要的层级;
  2. 性能浪费 :浏览器渲染 DOM 时,会解析这个多余的div,增加内存占用和渲染时间,尤其在复杂组件中,多层嵌套的无意义div会显著影响性能;
  3. 样式污染风险 :如果给这个div添加了样式或类名,可能会意外影响子元素,或被其他样式污染。

<></> 登场:解决问题的优雅方案

<></>就是为解决上述问题而生的,它还有一个正式的名字 ------Fragment(碎片)

(1)<></> 是什么?

<></>是 React 中Fragment组件的语法糖,两者完全等价:

jsx 复制代码
// 写法1:使用语法糖<></>
function UserInfo() {
  return (
    <>
      <h1>用户信息</h1>
      <p>姓名:张三</p>
      <p>年龄:25</p>
    </>
  );
}

// 写法2:使用完整的Fragment组件
import { Fragment } from 'react';

function UserInfo() {
  return (
    <Fragment>
      <h1>用户信息</h1>
      <p>姓名:张三</p>
      <p>年龄:25</p>
    </Fragment>
  );
}

这两种写法在功能上没有任何区别,最终都会被 React 处理为 "不生成实际 DOM 元素的容器"。

(2)它解决了什么问题?

简单说,<></>的核心作用是:在满足 JSX 语法要求(唯一父元素)的同时,不生成多余的 DOM 节点 。 对比使用div<></>的 DOM 结构:

  • DOM 结构更简洁,减少了浏览器的解析和渲染负担;
  • 避免了因多余div导致的样式冲突或布局问题;
  • 代码更优雅,不用再为了满足语法而添加无意义的元素。

Fragment 的 "隐藏技能":带 key 的场景

<></>虽然方便,但有一个限制:不能添加 key 属性 。而在列表渲染等场景中,我们需要给每个列表项添加key(React 要求列表项必须有唯一key以优化重渲染),这时就需要使用完整的Fragment组件:

jsx 复制代码
import { Fragment } from 'react';

function ArticleList({ articles }) {
  return (
    <div>
      {articles.map(article => (
        // 列表项必须有key,此时不能用<></>,需用Fragment
        <Fragment key={article.id}>
          <h2>{article.title}</h2>
          <p>{article.content}</p>
          <hr />
        </Fragment>
      ))}
    </div>
  );
}

为什么<></>不能加key?因为它是语法糖,设计初衷是简化最常见的 "无属性容器" 场景,而带key的场景相对特殊,因此 React 保留了完整Fragment组件的用法。

底层原理:JavaScript 的文档碎片(DocumentFragment)

React 的 Fragment 灵感来源于 JavaScript 原生的文档碎片(DocumentFragment) ,它是一种轻量级的 DOM 容器,不会被渲染到页面中,但可以临时存放 DOM 节点。

(1)原生文档碎片的用法

看一个原生 JS 的例子:假设你需要动态添加 100 个列表项到页面,如果直接操作 DOM,会触发 100 次重排重绘,性能很差。而使用文档碎片,可以批量操作,只触发一次重排:

javascript 复制代码
// 原生JS:用文档碎片优化DOM操作
const list = document.getElementById('list');
const fragment = document.createDocumentFragment(); // 创建文档碎片

for (let i = 0; i < 100; i++) {
  const li = document.createElement('li');
  li.textContent = `Item ${i}`;
  fragment.appendChild(li); // 先添加到文档碎片(不触发渲染)
}

list.appendChild(fragment); // 一次性添加到DOM(只触发一次重排)

关键点

  • 文档碎片是 "虚拟容器",不会出现在真实 DOM 树中;
  • 所有操作都在内存中完成,最后一次性添加到 DOM,大幅提升性能。

(2)React Fragment 与文档碎片的关系

React 的 Fragment 借鉴了文档碎片的思想:只组织元素,不生成额外 DOM。两者的核心区别是:

  • 文档碎片是原生 JS API,用于优化 DOM 操作;
  • React Fragment是 React 组件,用于解决 JSX 必须有根节点的问题。

总结:Fragment 的使用原则

  1. 优先使用<></> :当不需要key或其他属性时,用空标签语法更简洁;
  2. 需要key时用<Fragment> :在循环渲染或需要唯一标识的场景中使用;
  3. 避免无意义的 div:任何时候,当你想添加一个 "只是为了满足语法" 的 div 时,考虑用 Fragment 替代;
  4. 性能敏感场景考虑文档碎片:在大量 DOM 操作(如动态列表)时,结合原生文档碎片进一步优化。
相关推荐
大雷神31 分钟前
站在JS的角度,看鸿蒙中的ArkTs
开发语言·前端·javascript·harmonyos
杨荧6 小时前
基于大数据的美食视频播放数据可视化系统 Python+Django+Vue.js
大数据·前端·javascript·vue.js·spring boot·后端·python
cmdyu_6 小时前
如何解决用阿里云效流水线持续集成部署Nuxt静态应用时流程卡住,进行不下去的问题
前端·经验分享·ci/cd
WordPress学习笔记6 小时前
根据浏览器语言判断wordpress访问不同语言的站点
前端·javascript·html
yuanmenglxb20046 小时前
解锁webpack核心技能(二):配置文件和devtool配置指南
前端·webpack·前端工程化
小厂永远得不到的男人6 小时前
java 面试八股这一篇就够之java集合篇
面试·程序员
牛客企业服务7 小时前
AI面试系统助手深度评测:6大主流工具对比分析
数据库·人工智能·python·面试·职场和发展·数据挖掘·求职招聘
JefferyXZF7 小时前
Next.js 路由导航:四种方式构建流畅的页面跳转(三)
前端·全栈·next.js
啃火龙果的兔子7 小时前
React 多语言(i18n)方案全面指南
前端·react.js·前端框架
阿奇__7 小时前
深度修改elementUI样式思路
前端·vue.js·elementui