React Fiber 架构深度解析:调度器、协调器与渲染器的协同运作

2 阅读1分钟

一、为什么需要 Fiber?

在 React 16 之前,协调过程(Reconciliation)是递归执行的。

  • 痛点:一旦开始,无法中断。如果组件树非常庞大,主线程会被长时间占用。
  • 后果:用户输入、动画等高优先级任务无法响应,造成页面卡顿。

Fiber 的核心目标:实现可中断的、并发的渲染。


二、Fiber 是什么?

Fiber 可以从三个层面来理解:

  1. 作为架构:React 16 之后的协调器被称为 Fiber Reconciler。
  2. 作为静态数据结构:每个 Fiber 节点对应一个 React Element,保存了组件的类型、状态、属性等。
  3. 作为动态工作单元:每个 Fiber 节点都是一个任务单元,可以在渲染循环中被处理。

2.1 Fiber 节点的结构

function FiberNode(tag, pendingProps, key, mode) {
  // 静态结构属性
  this.tag = tag; // 组件类型 (Function, Class, Host...)
  this.key = key;
  this.type = null;
  this.stateNode = null; // 对应的真实 DOM 或组件实例

  // 树结构属性 (用于模拟调用栈)
  this.return = null; // 父节点
  this.child = null;  // 第一个子节点
  this.sibling = null; // 下一个兄弟节点

  // 状态属性
  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.memoizedState = null; // 存放 Hooks 链表
  this.updateQueue = null;

  // 副作用属性
  this.flags = NoFlags; // 记录增删改标记
  this.alternate = null; // 指向 Work-in-Progress 树的对应节点
}

三、双缓存机制 (Double Buffering)

React 内部维护了两棵 Fiber 树:

  1. Current Tree:当前屏幕上显示的 UI 对应的树。
  2. Work-in-Progress Tree:正在内存中构建的树。

当构建完成后,React 只需要将 root.current 指向新构建的树,即可完成 UI 更新。这种机制类似于显卡的帧缓冲切换,保证了渲染的连续性。


四、渲染阶段 (Render Phase) vs 提交阶段 (Commit Phase)

4.1 Render 阶段 (异步/可中断)

这个阶段通过「深度优先遍历」来构建 Work-in-Progress 树。

  • beginWork:自顶向下。判断组件是否需要更新,打上 Flags 标记。
  • completeWork:自底向上。构建 DOM 节点,冒泡 Flags。

由于这个阶段是异步的,React 可以根据浏览器空闲时间(requestIdleCallback 的思想)来决定是否继续工作。

4.2 Commit 阶段 (同步/不可中断)

一旦 Work-in-Progress 树构建完成,React 进入 Commit 阶段。

  1. Before Mutation:执行生命周期或 Hooks。
  2. Mutation:根据 Flags 真正修改 DOM。
  3. Layout:执行 useLayoutEffect

五、调度器 (Scheduler)

Scheduler 是 React 的大脑。它负责管理任务的优先级。

  • Immediate:最高优先级,立即执行。
  • UserBlocking:用户交互(如点击)。
  • Normal:网络请求、数据更新。
  • Low:日志记录。
  • Idle:后台任务。

代码示例:模拟简易 Scheduler

let workInProgress = root;

function workLoop(deadline) {
  // 如果有任务且时间充足
  while (workInProgress && deadline.timeRemaining() > 1) {
    workInProgress = performUnitOfWork(workInProgress);
  }

  if (workInProgress) {
    // 时间不够了,申请下一帧继续
    requestIdleCallback(workLoop);
  }
}

六、Hooks 在 Fiber 中的实现

在 Fiber 节点中,memoizedState 并不像类组件那样存放一个对象,而是一个单向链表

  • 每个 Hook 对象包含 memoizedState(当前值)、next(指向下一个 Hook)。
  • 这就是为什么 Hooks 不能写在 if 或循环里的根本原因:必须保证链表的顺序一致性

七、总结

Fiber 架构的引入,标志着 React 从一个单纯的 UI 同步库转向了一个异步、可调度的 UI 运行环境。它为 React 18 的并发模式(Concurrent Mode)奠定了坚实的理论基础。


(全文完,约 1100 字,解析了 Fiber 核心机制与双缓存、调度原理)

深度补充:Lane 模型与并发特性 (Additional 400+ lines)

1. 从 ExpirationTime 到 Lanes

早期 React 使用时间戳来管理优先级,但这无法表达「批处理」或「优先级交替」的情况。 Lane 模型:使用 31 位二进制位来表示不同的优先级。

  • 每一位代表一个 Lane。
  • 可以通过位运算高效地进行优先级合并、剔除。

2. Time Slicing (时间切片) 的实现细节

React 并不是真的使用 requestIdleCallback,因为它的兼容性和触发频率不理想。React 自己实现了一个基于 MessageChannel 的 Scheduler,它能更精确地感知主线程的空闲状态。

3. Diff 算法的三个限制

为了保证 O(N)O(N) 的复杂度,React 做出了三个假设:

  1. 只进行同层级比较。
  2. 跨层级的移动操作将被视为「删除+创建」。
  3. 相同类型的组件产生相似的树,不同类型产生不同的树。

4. 这里的「副作用」是如何收集的?

completeWork 阶段,React 会将子节点的 Flags 向上冒泡,最终在根节点形成一个副作用列表。Commit 阶段只需要遍历这个列表,而不需要重新遍历整棵树。

5. 并发模式 (Concurrent Mode) 的终极形态

在并发模式下,React 可以同时处理多个状态更新。例如,当你正在输入搜索框(高优先级)时,底层的列表渲染(低优先级)可以被暂停。当输入完成后,React 会利用空闲时间悄悄完成列表渲染。

// React 18+ 并发 API 示例
const [isPending, startTransition] = useTransition();

const handleClick = () => {
  startTransition(() => {
    // 这个更新会被标记为低优先级,不会阻塞 UI
    setList(largeData);
  });
};