Skip to content

React 原理解析——优先级调度

约 1475 字大约 5 分钟

react

2025-04-10

🚧 施工中...

React 的并发渲染能力依赖于其精细的优先级调度系统,Lanes 模型 是这一系统的基石。

Lanes 模型

它使用 31 位二进制掩码 表示不同优先级的更新任务,每个位称为一个 "Lane"(优先级通道)。

> 0b 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
                                                                 ^
                                                                最高优先级
> 0b 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
                                                               ^
                                                               第二优先级
> 0b 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1
                                                         ^     ^ ^
                                                         优先级组合
  • 每个 Lane 对应一个二进制位,共 31 个
  • 优先级从右向左递减(SyncLane 在 bit 2)
  • 相同类型任务复用 lane 组(如 TransitionLanes 含 14 个 lane)

Lanes 类型

  1. 同步优先级
    • SyncHydrationLane
    • SyncLane
  2. 连续输入优先级
    • InputContinuousHydrationLane
    • InputContinuousLane
  3. 默认优先级
    • DefaultHydrationLane
    • DefaultLane
  4. 过渡优先级
    • TransitionHydrationLane
    • TransitionLane, TransitionLane1 ~ TransitionLane14
  5. 重试优先级
    • RetryLanes
    • RetryLane1 ~ RetryLane4
  6. 空闲优先级
    • IdleHydrationLane
    • IdleLane
    • OffscreenLane
    • DeferredLane

源码位置:facebook/react / react-reconciler/src/ReactFiberLane.js#L39

Lanes 操作机制

优先级计算

React 使用位运算实现高效优先级管理:

// 获取最高优先级Lane(最右侧的1)
export function getHighestPriorityLane(lanes: Lanes): Lane {
  return lanes & -lanes // 补码技巧:-lanes = ~lanes + 1
}

// 获取等或更高优先级的Lanes集合
function getLanesOfEqualOrHigherPriority(lanes: Lanes): Lanes {
  const lowestIndex = 31 - clz32(lanes) // 计算前导零
  return (1 << (lowestIndex + 1)) - 1 // 生成掩码
}

位运算优势:

  • O(1)O(1) 时间复杂度完成优先级查询
  • 单次操作处理多个Lane(批量更新)
  • 内存紧凑(单数字存储完整状态)

调度状态机

React 使用三状态模型管理任务生命周期:

对应代码实现:

// 标记挂起任务
export function markRootSuspended(root: FiberRoot, suspendedLanes: Lanes) {
  root.suspendedLanes |= suspendedLanes
  root.pingedLanes &= ~suspendedLanes
}

// 标记数据到达
export function markRootPinged(root: FiberRoot, pingedLanes: Lanes) {
  root.pingedLanes |= root.suspendedLanes & pingedLanes
}

// 检测过期任务
export function markStarvedLanesAsExpired(root, currentTime) {
  let lanes = root.pendingLanes
  while (lanes) {
    const index = 31 - clz32(lanes)
    const lane = 1 << index
    if (root.expirationTimes[index] <= currentTime) {
      root.expiredLanes |= lane // 标记过期
    }
    lanes &= ~lane
  }
}

任务调度算法

调度决策流程

关键调度逻辑

export function getNextLanes(root, wipLanes, rootHasPendingCommit) {
  const pendingLanes = root.pendingLanes

  // 分层处理策略
  const nonIdlePendingLanes = pendingLanes & NonIdleLanes
  if (nonIdlePendingLanes !== NoLanes) {
    // 优先级1:未阻塞任务
    const unblocked = nonIdlePendingLanes & ~root.suspendedLanes
    if (unblocked !== NoLanes)
      return getHighestPriorityLanes(unblocked)

    // 优先级2:被ping任务
    const pinged = nonIdlePendingLanes & root.pingedLanes
    if (pinged !== NoLanes)
      return getHighestPriorityLanes(pinged)

    // 优先级3:需预热任务
    if (!rootHasPendingCommit) {
      const toPrewarm = nonIdlePendingLanes & ~root.warmLanes
      if (toPrewarm !== NoLanes)
        return getHighestPriorityLanes(toPrewarm)
    }
  }

  // 中断保护机制
  if (wipLanes !== NoLanes && wipLanes !== nextLanes) {
    const nextLane = getHighestPriorityLane(nextLanes)
    const wipLane = getHighestPriorityLane(wipLanes)
    if (nextLane >= wipLane)
      return wipLanes // 不中断当前渲染
  }
}

调度策略

  • 非空闲任务优先:确保用户交互及时响应
  • 状态分层处理:未阻塞任务 > 被ping任务 > 需预热任务
  • 渲染连续性保护:避免高优先级任务中断进行中的低优先级渲染

嵌套更新处理

React 通过纠缠(Entanglement)机制处理关联更新:

export function markRootEntangled(root, entangledLanes) {
  root.entangledLanes |= entangledLanes
  const entanglements = root.entanglements

  let lanes = root.entangledLanes
  while (lanes) {
    const index = 31 - clz32(lanes)
    const lane = 1 << index
    // 传播纠缠关系
    entanglements[index] |= entangledLanes
    lanes &= ~lane
  }
}

纠缠场景

  • 同一事件触发的多个更新
  • 父组件更新触发的子组件更新
  • useDeferredValue 关联更新

过期时间计算

function computeExpirationTime(lane, currentTime) {
  switch (lane) {
    case SyncLane: return currentTime + 250 // 250ms超时
    case DefaultLane: return currentTime + 5000 // 5s超时
    case TransitionLane1: return currentTime + 5000 // 5s超时
    case IdleLane: return NoTimestamp // 永不过期
  }
}

批量更新处理

export function upgradePendingLanesToSync(root, lanesToUpgrade) {
  root.pendingLanes |= SyncLane
  root.entangledLanes |= SyncLane

  let lanes = lanesToUpgrade
  while (lanes) {
    const index = 31 - clz32(lanes)
    const lane = 1 << index
    // 将多个Lane关联到SyncLane
    root.entanglements[SyncLaneIndex] |= lane
    lanes &= ~lane
  }
}

与Scheduler的协同