Fiber 结构和普通 VNode 区别
Date: February 10, 2025
Area: 原理
Question: Fiber 结构和普通 VNode 区别
Review: 25/11/27
本质差异
普通 VNode:
是UI结构的静态快照 ,一个轻量的 JavaScript 对象,描述了真实 DOM 应该是什么样子(标签名、属性、子节点等)。它的核心目的是通过 Diff 算法对比新旧 VNode 树,计算出最小变更,最后统一更新真实 DOM,以减少性能开销。
Fiber Node:
是 React 内部的工作单元 。Fiber 架构将复杂的渲染更新过程拆分成多个小任务单元 (每个 Fiber 节点就是一个单元),并赋予它们不同的优先级。这使得 React 可以在浏览器空闲时执行这些任务,高优先级的更新(如用户输入)可以中断低优先级的更新(如渲染大数据列表),从而实现增量渲染,极大提升应用的响应能力
| 特性维度 | 普通 VNode (Virtual DOM Node) | React Fiber Node |
|---|---|---|
| 本质目的 | UI 的静态描述,描述"是什么" | 动态的工作单元,描述"如何做" |
| 数据结构 | 通常为嵌套的树形结构 (通过 children属性连接) |
链表树 (通过 child, sibling, return指针连接) |
| 更新过程 | 同步递归,不可中断,一旦开始直到整棵树 Diff 完成 | 可中断的异步增量更新,将任务拆分成小单元,可暂停、继续和优先级调度 |
| 数据存储 | 主要存储与渲染相关的信息 (类型、属性、子节点) | 存储大量状态、副作用、优先级等信息,是承载组件状态和更新逻辑的实体 |
| 与实例关系 | 与组件实例或 DOM 元素无强关联 | 通过 stateNode指向类组件实例、DOM 节点或其他 React 元素类型 |
| 生命周期 | 短暂,每次渲染都会重新创建,旧树可被垃圾回收 | 持久化 ,在多次渲染间复用,通过 alternate指针连接当前树和 workInProgress 树 |
| 核心优化 | 通过 Diff 算法减少对真实 DOM 的直接操作 | 时间切片 (Time Slicing) 和 优先级调度,避免阻塞主线程 |
数据结构对比
普通 VNode(React 15 及之前):
通常是树形结构 。父节点通过 children属性引用子节点数组,形成一个嵌套的树。这种结构遍历时通常需要递归,一旦开始难以中断。
jsx
const vNode = {
type: 'div', // 节点类型(组件/原生标签)
props: { className: 'container' }, // 属性
children: [vNode1, vNode2], // 子节点(树形结构)
key: 'unique-id', // 优化 Diff 性能
// 无状态、调度、副作用信息
}
- 核心字段:仅包含 UI 描述相关属性(type、props、children)。
Fiber Node(React 16+):
是链表树结构 (单链表)。每个 Fiber 节点包含 child(第一个子节点)、sibling(下一个兄弟节点)和 return(父节点)指针。这种结构使得遍历可以不需要递归 ,只需循环即可,从而实现了可中断的深度优先遍历。
Case:

jsx
function App() {
return (
<div>
Hello
<span>World</span>
</div>
)
}
Fiber Node 数据结构:
jsx
export type Fiber = {
// --- 组件类型与标识 ---
tag: WorkTag, // 标记fiber的类型 (函数组件、类组件、宿主组件、Fragment等)[1,2](@ref)
key: null | string, // 唯一标识,用于协调过程中识别同级节点[1](@ref)
type: any, // 与fiber关联的函数/类/类型 (对于函数组件是函数本身,对于原生标签是字符串)[1](@ref)
elementType: any, // 元素类型,用于在协调期间保留身份标识
stateNode: any, // 对应的真实实例 (DOM节点、类组件实例或FiberRoot)[1,2](@ref)
// --- Fiber树结构指针 (链表结构) ---
return: Fiber | null, // 指向父级Fiber节点[2,3](@ref)
child: Fiber | null, // 指向第一个子Fiber节点[2,3](@ref)
sibling: Fiber | null, // 指向下一个兄弟Fiber节点[2,3](@ref)
index: number, // 在同级节点中的索引位置,用于Diff算法
// --- 数据与状态 ---
pendingProps: any, // 新的、尚未处理的props[1](@ref)
memoizedProps: any, // 上一次渲染时使用的props[1](@ref)
memoizedState: any, // 上一次渲染时使用的状态 (函数组件的hooks链表、类组件的state、HostRoot的state)[4,5](@ref)
updateQueue: mixed, // 存储状态更新和回调函数的队列[1](@ref)
// --- 副作用与更新标记 ---
flags: Flags, // 标记当前Fiber需要执行的副作用类型 (如Placement, Update, Deletion)[3,5](@ref)
subtreeFlags: Flags, // 标记子树中需要执行的副作用
deletions: Array<Fiber> | null, // 记录需要被删除的子Fiber节点
lanes: Lanes, // 优先级车道 (React 17+),表示任务的紧迫程度[1,2](@ref)
childLanes: Lanes, // 子树中的优先级车道
// --- 双缓存机制 ---
alternate: Fiber | null, // 指向当前树或workInProgress树中的对应节点,用于复用和比较[3,5](@ref)
};
核心扩展:
- 调度控制 :
lanes优先级、任务到期时间。 - 状态管理:Hooks 链表(函数组件)、类组件状态队列。
- 副作用追踪 :
effectTag标记和副作用链表。 - 遍历结构 :
child/sibling/return构成双向链表。
协调机制对比
| 流程 | VNode(Stack Reconciler) | Fiber Reconciler |
|---|---|---|
| 遍历方式 | 递归遍历(不可中断) | 循环遍历链表(可中断 + 恢复) |
| 任务调度 | 同步执行,阻塞主线程 | 异步分片,空闲时间执行 |
| 优先级控制 | 无 | Lane 模型(31 个优先级车道) |
| 副作用处理 | 统一提交 DOM 更新 | 构建副作用链表,分阶段提交 |
Fiber 两阶段提交:
- 协调阶段 (可中断):
- 增量构建 Fiber 树,标记副作用(
effectTag)。 - 通过
requestIdleCallback或 Scheduler 包分片执行。
- 增量构建 Fiber 树,标记副作用(
- 提交阶段 (同步不可中断):
- 遍历副作用链表,执行 DOM 操作和生命周期方法。
双缓存技术
这是 Fiber 架构的一个精妙之处。React 同时维护两棵 Fiber 树:
Current Tree:"真实 UI 对应的 Fiber Tree",代表当前屏幕上显示的状态。
WorkInProgress Tree:"正在内存中构建的 Fiber Tree",正在内存中构建的新状态。
两棵树通过 alternate指针相互连接。当 workInProgress树构建完成,在 Commit 阶段渲染后,React 会将 current指针指向它,于是 workInProgress树就变成了新的 current树。这种交换技术能确保更新的连贯性,避免中间状态对用户可见。

能力扩展示例
a. 支持 Hooks 状态管理
- Fiber 节点通过
memoizedState字段存储 Hooks 链表:
jsx
// 函数组件的 Hooks 链表
fiberNode.memoizedState = {
memoizedState: 'state value', // useState 的状态
next: {
// 下一个 Hook(如 useEffect)
memoizedState: { cleanup: fn },
next: null,
},
}
- VNode 无状态管理能力,仅描述 UI。
b. 优先级调度实战
-
高优先级任务抢占:
jsx// 用户输入触发高优先级更新 input.addEventListener('input', () => { React.startTransition(() => { setInputValue(e.target.value) // 低优先级 }) // 高优先级更新立即执行 }) -
VNode 架构无法实现任务中断和优先级插队。
c. 副作用批处理
-
Fiber 通过
effectList链表收集所有变更,统一提交:jsx// 提交阶段遍历 effectList let nextEffect = fiberRoot.firstEffect while (nextEffect) { commitWork(nextEffect) nextEffect = nextEffect.nextEffect } -
VNode 架构在 Diff 后直接操作 DOM,无批处理优化。
性能影响对比
| 场景 | VNode 架构 | Fiber 架构 |
|---|---|---|
| 大型组件树渲染 | 主线程阻塞导致掉帧 | 分片渲染,保持 UI 响应 |
| 高频更新(如动画) | 多次渲染合并困难 | 基于优先级合并或跳过中间状态 |
| SSR 水合(Hydration) | 全量同步处理 | 增量水合,优先交互部分 |