Fiber 架构
在 React 16 之前,更新虚拟 DOM 的过程称为 Stack Reconciler,这是一个递归的过程,在层级很深的时候,单次 diff 时间过长会让 JS 线程持续被占用,导致用户交互响应迟滞,页面渲染会出现明显的卡顿。
为了解决这种问题,React 团队基于 Fiber 架构重构了 React,使其能够将任务分片,划分优先级,同时能够实现类似于操作系统中对线程的抢占式调度。
面试必考:什么是 Fiber?为什么要引入 Fiber?
- 架构层面:Fiber 是 React 16 中新的协调引擎(Reconciler)。
- 数据结构层面:Fiber 是一个虚拟 DOM 节点,包含了组件的类型、DOM 节点信息、指针(指向父、子、兄弟节点,形成链表)。
- 动态工作单元层面:Fiber 节点保存了本次更新中该组件改变的状态、要执行的工作(被标记的 EffectTag)。
为什么引入?:为了解决 React 15 以前的“同步递归渲染”导致主线程阻塞的问题,实现了异步可中断的更新。
1. 浏览器的渲染帧与卡顿原因
由于主流的屏幕刷新率都在 60Hz,因此浏览器渲染一帧的时间必须控制在 16.7ms 内才能保证不掉帧。
浏览器每一帧都需要完成哪些工作?

- 处理用户交互
- 解析 js 脚本
- Begin frame。resize、scroll 等事件的处理
- rAF (
requestAnimationFrame) - 布局 (Layout)
- 绘制 (Paint)
当浏览器在一帧内(16.6ms)执行完上述任务后,如果还有剩余时间,这段时间被称为空闲期(Idle Period)。React Fiber 的底层调度器(Scheduler)就是利用了这种机制(React 内部自己实现了一个 requestIdleCallback 的 Polyfill,基于 MessageChannel)在空闲期执行被拆分的渲染任务。
2. Fiber Reconciler 的核心机制
Fiber 协调核心在于:可中断、可恢复 和 优先级。
新特性如下:
- 给不同类型的更新任务赋予优先级,能够暂停、终止和复用渲染任务。
- 把渲染任务拆分成块,匀到多帧。
将以前的 stack reconciler 拆分成两个阶段:Render 阶段 与 Commit 阶段。
- Render 阶段:在这个阶段会进行 VDOM 的 Diff 计算,找出需要更新的节点。这个过程是可被打断的,被拆分成一个个的小任务(Fiber),在每一帧的空闲期执行。
- Commit 阶段:这个阶段将 Render 阶段计算出的结果(effectList)应用到真实的 DOM 上。这个过程是同步执行、不可打断的,保证用户看到的 UI 是一致的。
优先级抢占逻辑:
- 每个更新任务都会赋予一个优先级(Lane 模型)。
- 当高优先级的更新任务(B)抵达调度器时,如果当前正在执行低优先级任务(A),处于当前 Reconciler 层的 A 任务会被中断。
- 当 B 任务完成渲染后,新一轮调度开始,之前被中断的 A 任务将会被重新推入 Reconciler 层,继续它的渲染(可恢复)。
3. Fiber Node 的数据结构
React Fiber 把更新过程碎片化,以 Fiber Node 为一个工作单元。
一个虚拟 DOM 节点对应一个 Fiber Node。为了实现可中断和恢复,Fiber 将树形结构改造成了单链表结构。
type Fiber = {
// === 基础信息 ===
tag: WorkTag; // 标识不同的组件类型 (FunctionComponent, ClassComponent...)
key: null | string;
elementType: any; // react 元素类型
type: any; // 组件函数或类
stateNode: any; // 该 fiber 节点对应的真实 DOM 节点或组件实例
// === 链表结构指针 ===
return: Fiber | null; // 指向父节点
child: Fiber | null; // 指向第一个子节点
sibling: Fiber | null; // 指向下一个兄弟节点
index: number;
// === 工作状态 ===
pendingProps: any; // 正在计算的新 props
memoizedProps: any; // 上次渲染的旧 props
updateQueue: UpdateQueue<any> | null; // 状态更新队列
memoizedState: any; // 也就是 Hooks 链表存放的地方
// === 副作用 (DOM diff相关) ===
flags: Flags; // (以前叫 effectTag) 记录这个节点需要执行的操作:如新增、删除、更新
nextEffect: Fiber | null; // 指向下一个有副作用的 fiber
// === 双缓冲机制 ===
alternate: Fiber | null; // 指向 current 树中的对应节点
};
面试考点:为什么 Fiber 要采用单链表结构(
return,child,sibling)而不是传统的树递归? 传统的树递归一旦开始就无法暂停(调用栈由 JavaScript 引擎控制,无法主动保存当前执行到哪一个节点)。单链表结构使得 React 可以在任意一个 Fiber 节点暂停工作,把控制权交还给浏览器,并且在恢复时通过return向上找到父节点,或者通过sibling找到兄弟节点,完美实现了渲染状态的保存和恢复。
4. 双缓冲机制 (Double Buffering)
这也是 Fiber 架构中的高级考点。
React 内部同时存在两棵 Fiber 树:
current树:对应当前屏幕上显示内容的 Fiber 树。workInProgress树:正在内存中构建的 Fiber 树。
工作流:
当有更新发生时,React 会在内存中创建一棵新的 workInProgress 树。所有的 Diff 和 DOM 变更标记都是在这棵新树上进行的,这个过程可中断。当新树构建完成并且进入 Commit 阶段应用到真实 DOM 后,workInProgress 树就会通过一根指针(alternate)替换为新的 current 树。
好处:
这就像计算机图形学中的“双缓冲技术”,如果在直接显示在屏幕上的画布上作画,用户会看到撕裂和闪烁。而在内存中的隐藏画布(workInProgress)上作画,画完后瞬间替换(Commit 阶段),保证了用户看到的 UI 永远是完整和一致的,并且两棵树的节点可以通过 alternate 属性相互复用,极大节省了内存分配开销。
补充:早期 React(16/17)用
expirationTime(过期时间)表示优先级,React 18 起改为更灵活的 Lane(车道)模型,用二进制位表示多个并存的优先级。
Fiber 树示意图如下:


面试题:双缓冲树机制带来的三大优势是什么?
- 实现无缝更新:所有的 Diff 和状态计算都在内存中的
workInProgress树进行,等全部计算完毕并在 Commit 阶段挂载到 DOM 后,再瞬间替换指针。避免了因为中断机制导致屏幕上出现“渲染一半”的半成品 UI。- 极大的性能复用:
workInProgress树节点在创建时,会尽可能复用current树节点中的属性,极大减少了对象的频繁创建和垃圾回收 (GC) 的时间开销。- 异常隔离兜底:如果
workInProgress树在 Render 阶段计算时发生严重报错,React 可以直接丢弃这棵树。由于真实的 DOM 依然由current树控制,页面不会白屏崩溃,只是这次更新失败了而已。