文章如果错误偏差,烦请及时批评指正
一、为什么要使用 <Fragment>
?
因为在 React
中,组件必须 返回单个根元素
。当我们尝试直接返回相邻的 JSX 元素时:
javascript
function BrokenComponent() {
return (
<h1>标题</h1>
<p>正文内容</p>
);
}
// 报错:Adjacent JSX elements must be wrapped in an enclosing tag
传统解决方案是使用
包裹,但这会带来 三大问题:
1、破坏布局结构:多余的 DOM 节点可能干扰 CSS 布局(如 Flex/Grid)
2、性能损耗:增加无意义的 DOM 层级
3、语义污染:无关的
影响 HTML 语义化
二、基础用法:两种写法全解析
- 显式写法(推荐场景:需要 key 属性时)
当我们遍历数组时,需要添加 key 属性时,我们需要显式的使用
javascript
import React, { Fragment } from 'react';
function ListItems() {
return (
<Fragment>
<li>第一项</li>
<li>第二项</li>
<li>第三项</li>
</Fragment>
);
}
- 简写语法(空标签)
javascript
function ShortSyntax() {
return (
<>
<h2>欢迎界面</h2>
<button>开始使用</button>
</>
);
}
三、实际开发过程中的应用场景
- 列表渲染(必须使用 key)
javascript
function UserList({ users }) {
return users.map(user => (
<Fragment key={user.id}>
<dt>{user.name}</dt>
<dd>{user.email}</dd>
</Fragment>
));
}
// 正确:Fragment 支持 key 属性
// 错误:空标签语法 <></> 不能添加属性
- 条件渲染
javascript
function AuthButton({ isLoggedIn }) {
return (
<Fragment>
{isLoggedIn ? (
<button>退出登录</button>
) : (
<Fragment>
<button>登录</button>
<button>注册</button>
</Fragment>
)}
</Fragment>
);
}
- 表格结构
需要注意添加 标签的位置
javascript
function TableData() {
return (
<table>
<tbody>
<tr>
<Fragment>
<td>单元格1</td>
<td>单元格2</td>
</Fragment>
</tr>
</tbody>
</table>
);
}
// 注意:直接包裹 <tr> 会破坏表格结构
- 组合组件
javascript
function Layout() {
return (
<>
<Header />
<MainContent />
<Footer />
</>
);
}
- 高阶组件(HOC)
javascript
const withLogger = (WrappedComponent) => {
return (props) => (
<Fragment>
<ConsoleLogger />
<WrappedComponent {...props} />
</Fragment>
);
};
- 渲染数组
javascript
function ArrayRender() {
return (
<>
{['A', 'B', 'C'].map((item) => (
<Fragment key={item}>
<span>{item}</span>
<br />
</Fragment>
))}
</>
);
}
四、深度原理剖析
源码实现(简化版):
javascript
const Fragment = Symbol.for('react.fragment');
function createFragment(children) {
return {
$$typeof: Symbol.for('react.element'),
type: Fragment,
props: { children },
key: null
};
}
React 在调和(Reconciliation)阶段会:
识别 Fragment 类型
直接平铺其子节点
不创建真实 DOM 节点
五、开发者常遇到问题
1、样式丢失陷阱
javascript
// 错误示例:
<div className="parent">
<>
<Child style={{ margin: 10 }} />
</>
</div>
// 正确:直接在父级设置样式容器 无脑简写导致 key 缺失
javascript
// 错误示例:
{items.map(item => (
<> // 错误 缺少 key
<span>{item.name}</span>
<span>{item.value}</span>
</>
))}
2、多层 Fragment 嵌套
javascript
// 不良实践:
<>
<>
<ComponentA />
<ComponentB />
</>
<ComponentC />
</>
// 建议:单层 Fragment 保持结构清晰
3、与第三方库的冲突
javascript
// 某些动画库(如 Framer Motion)需要真实 DOM:
<motion.div>
<Fragment> //错误 动画失效
{/* content */}
</Fragment>
</motion.div>
4、开发工具调试困惑
Fragment 在 React DevTools 中显示为 ,可通过设置显示名称优化:
javascript
const MyFragment = ({ children }) => <Fragment>{children}</Fragment>;
MyFragment.displayName = 'MyFragment'; // 调试更友好
七、注意事项
1、优先使用空标签语法:<>...</>
简洁直观
2、需要 key
时切回显式 Fragment
:列表项必须添加
3、避免深度嵌套:超过 3 层应考虑组件拆分,考虑组件的单一性易维护性
4、组件中需要结合 TypeScript
增加类型检查,避免使用人员传入不符合类型的数据
javascript
const FragmentWrapper: React.FC<{ children: React.ReactNode }> =
({ children }) => <>{children}</>;
5、性能敏感场景实测:大数据列表优先选择 Fragment
6、最后:当你在纠结是否使用 Fragment
时,先问自己两个问题:
这个容器是否需要任何样式或交互?
添加 DOM 节点是否会影响父级布局?
满足这两个条件,可以放心使用