Vue Fragment vs React Fragment - 详解
前言
Vue 3 的 Fragment 与 React 的 Fragment 在设计理念上非常相似,但在实现和使用方式上存在一些差异。
一、概念对比:Vue Fragment vs React Fragment
| 特性 | Vue 3 Fragment | React Fragment |
|---|---|---|
| 支持多根节点 | ✅ 是 | ✅ 是 |
| 渲染为真实 DOM | ❌ 否(虚拟容器) | ❌ 否 |
| 使用方式 | 默认启用,无需引入额外组件 | 需要显式使用 <React.Fragment> 或 <> |
| 插槽支持 | ✅ 插槽内容可返回多个根节点 | ❌ 插槽非内建机制,需要 props.children |
| 实现机制 | 编译时自动转为 Fragment 虚拟节点 | JSX 语法 sugar,转为 React.Fragment |
| 额外属性传递 | ❌ 不支持属性 | ✅ React.Fragment key 可用于列表 |
二、使用示例对比
✅ Vue 3 中使用 Fragment
Hello
World
Vue 编译器自动将其转换为:
return createVNode(Fragment, null, [
h('h1', 'Hello'),
h('p', 'World')
])
✅ React 中使用 Fragment
import React from 'react'
function App() {
return (
<
>
<h1>Hello<
/h1>
<p>World<
/p>
<
/>
)
}
JSX 编译后会变成:
React.createElement(React.Fragment, null,
React.createElement('h1', null, 'Hello'),
React.createElement('p', null, 'World')
)
三、差异解析
1. 使用方式
- Vue:Fragment 是内建的,无需导入;天然支持
- React:需要写
React.Fragment或<>明确表达
2. 传递属性(如 key)
React Fragment 可写
key,常用于.map()渲染列表items.map(item => ( <React.Fragment key={item.id }> <div> {item.name }< /div> <div> {item.value }< /div> < /React.Fragment> ))Vue Fragment 是编译时产物,不支持
:key绑定在 Fragment 上(需要用<template>+v-for来处理)
3. 插槽系统
- Vue 拥有完整的插槽(slots)系统,可让父组件传递多个根节点给子组件。
- React 使用
props.children,但无法像 Vue 插槽一样传具名、作用域插槽。
✅ 总结:选择建议
| 使用情境 | Vue Fragment | React Fragment |
|---|---|---|
| 多根节点输出 | ✅ 推荐 | ✅ 推荐 |
| 插槽内容多节点 | ✅ 更强插槽能力 | ❌ 插槽不支持 |
| 列表渲染 key | ❌ 不支持 | ✅ 可使用 key |
| 编写体验 | ✅ 自动优化 | ✅ JSX 精简语法 |
小结
- Vue Fragment:自动启用、插槽友好,专为模板系统设计。
- React Fragment:显式使用,更偏向 JavaScript 逻辑与手动控制。
底层行为与性能表现
深入对比 Vue Fragment vs React Fragment 的底层行为与性能表现,尤其在真实渲染 DOM、组件嵌套复杂度、渲染效率等方面,为你在构建大型或跨框架组件时提供参考。
一、Fragment 的真实 DOM 行为
| 项目 | Vue 3 Fragment | React Fragment |
|---|---|---|
| 是否渲染为 DOM | ❌ 不会渲染为任何 HTML 标签 | ❌ 不会渲染任何标签 |
| 结构影响 | 真实 DOM 中不产生额外父节点 | 同样不会产生额外父节点 |
| 插槽结构 | Vue 插槽内部也允许 Fragment | React children 中返回数组或 Fragment |
✅ 示例对比
Vue DOM 结构(组件模板内)
Hi
Bye
渲染后 DOM 结构:
<h1>Hi</h1>
<p>Bye</p>
React DOM 结构
<>
Hi
Bye
渲染后 DOM 结构相同。
⚙️ 二、编译 & 渲染机制底层差异
| 点位 | Vue 3 | React |
|---|---|---|
| 模板处理方式 | Vue 模板编译为 render 函数,再转为 VNode 树 | JSX 直接转为 React.createElement 调用 |
| Fragment 实现 | createVNode(Fragment, ...),Fragment 是特殊类型 | React.Fragment 是一个特殊组件 |
| Diff 算法 | 基于 block tree 和优化 patchFlag | Fiber 架构,递归遍历 + 优先级调度 |
✅ 示例:VNode 对象结构
Vue Fragment vnode 示例
{
type: Fragment,
children: [
{ type: 'h1', children: 'Hello'
},
{ type: 'p', children: 'World'
}
]
}
React Fragment vnode 示例
{
type: React.Fragment,
props: {
children: [
{
type: 'h1', props: {
children: 'Hello'
}
},
{
type: 'p', props: {
children: 'World'
}
}
]
}
}
三、性能对比(渲染效率)
✅ 相同点
- Fragment 本质上都不会带来额外 DOM,DOM 树更轻、更快。
- 两者都避免了冗余
<div>,在大量渲染场景中可以提升性能。
不同点
| 点 | Vue 3 | React |
|---|---|---|
| 静态提升 | Vue 模板编译可分析哪些节点是静态的,进行提升 | JSX 写法需手动避免不必要更新(如 memo、useMemo) |
| 渲染调度 | Vue 是同步递归 + PatchFlag 进行精准更新 | React 使用 Fiber 架构 + 优先级更新 |
| Fragment diff 优化 | Vue 在 block tree 中有特殊处理 | React Fragment 会参与 Fiber diff,但性能接近普通节点 |
实测结论(单个 Fragment 中渲染 1 万行元素)
| 框架 | 首次渲染时间 | 更新耗时 | 内存占用 |
|---|---|---|---|
| Vue 3 | ✅ 更快初始化(依赖模板优化) | 快速 diff(patchFlag) | 较低 |
| React 18 | 稍慢初始化 | 若使用 memo 优化后接近 Vue | 稍高 |
四、实际应用建议(跨框架组件)
| 场景 | 推荐策略 |
|---|---|
| 大型组件渲染性能 | Vue 更适合静态模板多的场景;React 需配合 memo / useCallback |
| 插槽式组件库(如弹窗、表单) | Vue 插槽 + Fragment 更强大(作用域插槽 + 多根支持) |
| 表格类大数据渲染 | 两者性能接近,但 Vue 可借助 patchFlag 更轻量 |
| 跨平台 UI 框架 | 建议抽象 Fragment 逻辑,避免过度依赖其结构行为 |
✅ 总结
| 对比项 | Vue Fragment | React Fragment |
|---|---|---|
| 是否自动启用 | ✅ 是(无须声明) | ❌ 否(需 <></>) |
| 是否支持 key | ❌ 不支持(v-for 中需用 template) | ✅ 支持(<Fragment key="">) |
| 插槽支持 | ✅ 强(具名、多根、作用域) | ❌ 无原生插槽机制 |
| 编译优化 | ✅ 静态提升 + patchFlag | ❌ 靠开发者优化 |
| Fiber 架构支持 | ❌ 无(同步渲染) | ✅ 有(支持并发调度) |
浙公网安备 33010602011771号