Fiber(React Fiber 架构)
React 16+ 的核心数据结构和调度架构,将组件树表示为链表结构的 Fiber 节点,使渲染工作可被拆分为小单元(work unit),实现可中断渲染、优先级调度和时间切片。
核心概念
Fiber 是 React 16 引入的重写架构,解决了 React 15 Stack Reconciler 的根本问题——递归 diff 不可中断,大组件树的 diff 会长时间阻塞主线程。
Fiber 的核心设计:
- 数据结构:每个组件/DOM 节点对应一个 Fiber 节点,节点通过
child、sibling、return三个指针组成链表树。链表结构的关键优势是遍历可以在任意节点暂停和恢复——这是 Stack Reconciler 的递归调用栈做不到的 - 工作单元:每个 Fiber 节点的处理(
beginWork→completeWork)是一个独立的 work unit,处理完一个节点后可以检查是否需要让出主线程 - 双缓冲:维护 current 树和 workInProgress 树两份 Fiber 树。更新时在 workInProgress 上操作,commit 阶段原子性地切换指针,保证用户看到的 DOM 始终一致
Fiber 节点的关键字段
FiberNode {
tag // 节点类型(FunctionComponent、HostComponent 等)
type // 组件函数或 DOM 标签名
stateNode // 真实 DOM 节点引用
memoizedState // hooks 链表头指针
memoizedProps // 上次渲染的 props
pendingProps // 待处理的新 props
flags // 副作用标记(Placement、Update、Deletion 等)
lanes // 该节点待处理的更新优先级
child / sibling / return // 链表指针
alternate // 指向另一棵树中的对应节点(双缓冲)
}
渲染流程
Fiber 架构将渲染分为两个阶段:
Render 阶段(可中断):
- 调度器从根节点开始,选择当前最高优先级的 lanes 子集
- 选择
workLoopSync(不可中断)或workLoopConcurrent(可中断)——这是调度器的最后一个决策点 performUnitOfWork逐个处理 Fiber 节点:beginWork执行组件函数/diff 子节点,completeWork收集 DOM 操作- 并发模式下,每个 work unit 结束后检查
shouldYield(),如有更高优先级任务则让出主线程
Commit 阶段(不可中断):
beforeMutation:执行 DOM 读操作(如getSnapshotBeforeUpdate)mutation:执行 DOM 写操作(插入、更新、删除),纯写阶段,不可插入读操作layout:执行useLayoutEffect,此时 DOM 已更新,允许开发者同步读写 DOM(如测量元素尺寸后调整布局)
两层读写分离大幅降低了 layout thrashing 风险:beforeMutation(读)和 mutation(写)之间实现了完全的读写隔离,消除了这两个阶段内的 forced synchronous layout;layout 阶段是一个受控的"读写混合区",但它发生在所有 mutation 完成之后,最多触发一次额外 layout 计算,不会产生交替 thrashing(O(N) 次 forced layout 的性能灾难)。相比之下,Solid 的 effect 内部没有架构级读写分离,开发者在 effect 中交替读写 DOM 会直接触发 forced layout。
注意:Render 阶段的可中断调度(时间切片 / Lanes 优先级)和 Commit 阶段的读写分离是两个正交的优化——前者解决"长 render 任务不卡主线程",后者解决"DOM 操作不触发 forced layout"。
与其他框架的对比
Fiber 架构的复杂度是 React 响应式模型的必然结果。React 没有细粒度状态追踪,setState 触发的更新可能涉及整棵子树的重新执行和 diff——工作量天然较大。Fiber 的可中断渲染和时间切片本质上是在说:"既然每次更新都很重,至少让我们把重活拆开做,别一口气卡住主线程。"
同时,较长的渲染管线也提供了架构空间来实现 commit 三阶段的读写分离——管线越长,越有余地在管线内部做精细的阶段划分。Solid 和 Svelte 的管线极短(signal → effect → DOM 操作),没有架构空间插入独立的读写分离阶段。管线长度不只是性能开销,也是优化能力。
Vue 选择了不同的折中——响应式追踪到属性级,组件级重渲染的工作量通过 Patch Flags 控制在较低水平,微任务队列批处理已足够,不需要 Fiber 级别的可中断调度。Vue 的 nextTick 提供了"建议性"的读写分离时机,但不像 React 那样在架构层面强制隔离。
卸载路径的复杂度
Fiber 的卸载(commitDeletionEffects)需要递归遍历整棵 Fiber 子树,按 child → sibling → return 顺序逐个触发 useEffect 的 cleanup、ref 清理、Suspense 边界处理等。这比 Solid 的 owner 树 dispose(直接调 onCleanup 回调)和 Vue 的 VNode 递归 unmount 都要复杂——因为 Fiber 节点上挂载的状态种类最多(hooks 链表、effect 链、ref、context 等)。
引用本术语的文章