React Fiber 架构

理解 Fiber 的链表结构、时间切片和可中断渲染机制

问题

解释 React Fiber 架构的设计原理,包括时间切片、链表结构和可中断渲染。

解答

为什么需要 Fiber

React 15 使用递归遍历虚拟 DOM 树(Stack Reconciler),一旦开始就无法中断,长时间占用主线程会导致页面卡顿。

// React 15 的递归协调(不可中断)
function reconcile(element, container) {
  // 递归处理所有子节点,无法中断
  element.children.forEach(child => {
    reconcile(child, container);
  });
}

Fiber 节点结构

Fiber 将树结构改为链表,每个节点通过三个指针连接:

// Fiber 节点结构
const fiber = {
  // 节点类型信息
  type: 'div',
  key: null,
  
  // 链表指针
  return: parentFiber,    // 父节点
  child: firstChildFiber, // 第一个子节点
  sibling: nextFiber,     // 下一个兄弟节点
  
  // 状态相关
  stateNode: domElement,  // 对应的真实 DOM
  pendingProps: {},       // 新的 props
  memoizedProps: {},      // 上次渲染的 props
  memoizedState: {},      // 上次渲染的 state
  
  // 副作用
  flags: NoFlags,         // 标记需要执行的操作
  nextEffect: null,       // 下一个有副作用的节点
};

链表遍历方式

// Fiber 树的遍历顺序:深度优先
function performUnitOfWork(fiber) {
  // 1. 处理当前节点
  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;
  }
  
  return null;
}

时间切片与可中断渲染

// 工作循环(简化版)
let workInProgress = null;
let shouldYield = false;

function workLoop(deadline) {
  // 有剩余时间且有待处理的工作
  while (workInProgress && !shouldYield) {
    // 执行一个工作单元
    workInProgress = performUnitOfWork(workInProgress);
    
    // 检查是否需要让出控制权(时间切片)
    shouldYield = deadline.timeRemaining() < 1;
  }
  
  // 还有工作未完成,请求下一次调度
  if (workInProgress) {
    requestIdleCallback(workLoop);
  } else {
    // 所有工作完成,提交更新
    commitRoot();
  }
}

// 启动调度
requestIdleCallback(workLoop);

双缓冲机制

// React 维护两棵 Fiber 树
const root = {
  current: currentFiber,      // 当前显示的树
  workInProgress: wipFiber,   // 正在构建的树
};

// 提交阶段:交换两棵树
function commitRoot() {
  // 执行 DOM 操作
  commitWork(root.workInProgress);
  
  // 交换指针
  root.current = root.workInProgress;
  root.workInProgress = null;
}

优先级调度

// 不同更新有不同优先级
const ImmediatePriority = 1;   // 同步,最高优先级
const UserBlockingPriority = 2; // 用户交互
const NormalPriority = 3;       // 普通更新
const LowPriority = 4;          // 低优先级
const IdlePriority = 5;         // 空闲时执行

// 高优先级可以打断低优先级
function ensureRootIsScheduled(root) {
  const nextLanes = getNextLanes(root);
  const existingCallbackPriority = root.callbackPriority;
  
  // 新任务优先级更高,取消旧任务
  if (newCallbackPriority > existingCallbackPriority) {
    cancelCallback(existingCallback);
  }
  
  // 调度新任务
  scheduleCallback(newCallbackPriority, performConcurrentWorkOnRoot);
}

关键点

  • 链表结构:通过 child、sibling、return 三个指针将树转为链表,支持随时中断和恢复
  • 时间切片:每个 Fiber 节点是一个工作单元,处理完一个就检查剩余时间,没时间就让出主线程
  • 双缓冲:维护 current 和 workInProgress 两棵树,构建完成后直接切换,避免中间状态
  • 优先级调度:不同更新有不同优先级,高优先级任务可以打断低优先级任务
  • 两个阶段:render 阶段可中断(构建 Fiber 树),commit 阶段不可中断(操作 DOM)