React Fiber原理解释

499 阅读3分钟

React Fiber 通过重新设计协调(Reconciliation)过程的底层架构,实现了可中断的异步渲染。以下是其核心实现原理:


1. Fiber 数据结构

每个 React 组件对应一个 Fiber 节点,形成链表树结构,包含以下关键信息:

  • 组件类型(如 divMyComponent
  • 状态和 Props
  • 指针child(第一个子节点)、sibling(下一个兄弟节点)、return(父节点)
  • 工作状态alternate(指向当前节点和正在构建的副本,用于双缓冲)
  • 副作用标记(如插入、更新、删除)
interface Fiber {
  tag: WorkTag;          // 组件类型(函数组件、类组件等)
  key: string | null;
  type: any;             // 组件对应的DOM类型或构造函数
  stateNode: any;        // 对应的真实DOM或组件实例
  pendingProps: Props;   // 新Props
  memoizedProps: Props;  // 当前Props
  memoizedState: any;    // 当前State(如Hooks链表)
  alternate: Fiber | null; // 指向另一棵树中的对应节点
  effectTag: SideEffectTag; // 副作用标记(如Placement、Update)
  nextEffect: Fiber | null; // 指向下一个有副作用的Fiber节点
  // ...其他字段
}

2. 可中断的遍历机制

(1) 链表遍历替代递归栈

  • 传统递归问题:递归调用栈不可中断,一旦开始必须执行到底。

  • Fiber 解决方案:将组件树转换为链表结构(通过 childsibling 指针),通过循环遍历(while)替代递归。

    function workLoop(deadline) {
      while (currentFiber && hasRemainingTime(deadline)) {
        currentFiber = performUnitOfWork(currentFiber);
      }
      if (!currentFiber) commitRoot(); // 提交变更
      else requestIdleCallback(workLoop); // 继续下一次循环
    }
    

(2) 时间分片(Time Slicing)

  • 任务拆分:将渲染任务拆分为多个 工作单元(Unit of Work) ,每个单元对应一个 Fiber 节点的处理。

  • 中断检查:在浏览器每一帧的 空闲时间(Idle Period)  执行任务,通过 requestIdleCallback(或 React 自研的 Scheduler)判断剩余时间:

    function performUnitOfWork(fiber) {
      // 1. 开始工作(如Diff比较、生命周期调用)
      beginWork(fiber);
      // 2. 如果有子节点,优先处理子节点
      if (fiber.child) return fiber.child;
      // 3. 没有子节点则处理兄弟节点或回溯父节点
      let nextFiber = fiber;
      while (nextFiber) {
        completeWork(nextFiber);
        if (nextFiber.sibling) return nextFiber.sibling;
        nextFiber = nextFiber.return;
      }
    }
    
  • 中断恢复:若时间不足,保存当前遍历位置(currentFiber),下次从断点继续。


3. 双缓冲与副作用收集

  • 双缓冲技术:通过 alternate 属性维护两棵 Fiber 树:

    • Current Tree:当前已渲染的树。
    • WorkInProgress Tree:正在构建的新树。
  • 副作用列表:在遍历过程中收集需要更新的节点(通过 effectTag 标记),形成单向链表(nextEffect),提交阶段一次性处理。


4. 优先级调度

React 根据更新来源(如用户交互、网络响应)分配不同优先级:

  • Immediate:需立即处理(如输入事件)。
  • UserBlocking:用户交互相关(如动画)。
  • Normal:普通数据更新。
  • Low:低优先级任务(如日志上报)。
  • Idle:空闲时处理。

通过优先级调度,高优先级任务可中断低优先级渲染。


5. 示例:中断与恢复流程

  1. 初始状态:开始渲染组件树,遍历到 Fiber 节点 B。

    AB → C
    
  2. 中断发生:用户点击按钮触发高优先级更新。

    • React 暂停当前渲染,保存断点(当前处理到 B)。
    • 优先处理点击事件的回调,生成新的更新。
  3. 恢复渲染:高优先级任务完成后,重新从断点(B)继续遍历,或根据新更新重新协调。


总结

机制实现方式
数据结构链表结构的 Fiber 节点,支持断点恢复。
任务拆分将渲染拆分为可中断的工作单元,通过循环遍历替代递归。
时间分片利用浏览器空闲时间执行任务,避免阻塞主线程。
双缓冲与副作用通过两棵树和副作用列表保证渲染一致性。
优先级调度动态调整任务优先级,确保交互响应性。

通过以上设计,React Fiber 实现了:

  • 可中断渲染:避免长时间任务阻塞主线程。
  • 增量渲染:逐步将更新应用到界面,提升流畅度。
  • 并发模式支持:为未来功能(如 Suspense、并发渲染)奠定基础。