Skip to content

Lane

在 React 中,Lane 是一个表示优先级的概念,用于在更新过程中控制不同组件的更新顺序。Lane 是一个 32 位的二进制数,每一位代表一个优先级。 其 1 的位置越靠近右边,优先级越高。

js
export const TotalLanes = 31;

// 没有车道的标志位
export const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000;

// 同步车道的标志位  1 在最右边,优先级最高
export const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000001;

// 连续输入(如滚动、拖拽、用户输入)、水合 等有较高的响应性和流畅度要求的事件
export const InputContinuousHydrationLane: Lane = /*    */ 0b0000000000000000000000000000010;
// 主要与连续性的用户输入操作相关
export const InputContinuousLane: Lane = /*             */ 0b0000000000000000000000000000100;

// 水合车道的标志位
export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000000001000;
// 普通的渲染车道的标志位
export const DefaultLane: Lane = /*                     */ 0b0000000000000000000000000010000;

// Transition 水合相关 车道的标志位
const TransitionHydrationLane: Lane = /*                */ 0b0000000000000000000000000100000;
const TransitionLanes: Lanes = /*                       */ 0b0000000001111111111111111000000;
const TransitionLane1: Lane = /*                        */ 0b0000000000000000000000001000000;

const RetryLanes: Lanes = /*                            */ 0b0000111110000000000000000000000;
const RetryLane1: Lane = /*                             */ 0b0000000010000000000000000000000;

export const SomeRetryLane: Lane = RetryLane1;

export const SelectiveHydrationLane: Lane = /*          */ 0b0001000000000000000000000000000;

const NonIdleLanes: Lanes = /*                          */ 0b0001111111111111111111111111111;

export const IdleHydrationLane: Lane = /*               */ 0b0010000000000000000000000000000;
export const IdleLane: Lane = /*                        */ 0b0100000000000000000000000000000;

// OffscreenLane 是一个特殊的车道,用于表示在 React 的并发模式下,某些组件可能处于离屏状态 其优先级很低
export const OffscreenLane: Lane = /*                   */ 0b1000000000000000000000000000000;

重要概念

  • Lanes 是一个表示多个车道的二进制数。
  • Lane 是一个表示单个车道的二进制数。
  1. 空闲( ~NonIdleLanes )和 非空闲( NonIdleLanes)
  • 空闲( ~NonIdleLanes )

表示的其 lane 的标识位为 最左边 3 位的车道事件, 如 IdleLane 、 OffscreenLane, 其优先级很低,不需要及时的去处理

  • 非空闲( NonIdleLanes)

表示的其 lane 的标识位为 最右边 29 位的车道事件, 如 DefaultLane 、 DefaultHydrationLane, 其优先级很高,需要及时的去处理

其中有几个比较重要的 如 SyncLane(标识位为 1,优先级最高) , DefaultLane(标识位为 16,优先级中等)

  1. HostRoot 中几个重要属性
  • 待处理车道( pendingLanes )

我们每次触发的更新 Update 都会生成一个新的 lane , 这个 lane 会被添加到 pendingLanes 中

  • 挂起车道( suspendedLanes )

  • root.expiredLanes(饥饿撤掉)

饥饿任务的 lane 的合集

每一个任务都有其对应的过期时间,当高优先级任务执行过长后,保证低优先级任务不会永远挂起,所以当成为饥饿 lane 的时候,会优先执行

在 Concurrent 模式下,React 会将一些组件标记为“挂起”,这些组件的更新会被延迟到下一个渲染周期中执行。 这些被标记为“挂起”的组件的 lane 会被添加到 suspendedLanes 中。

getNextLanes

依据 FiberRoot 和当前正在处理的车道(wipLanes)来确定下一组需要处理的更新车道(Lanes)。

在 React 的并发模式里,更新操作被赋予不同优先级的车道,该函数会综合各类因素挑选出合适的更新车道进行处理。

  • 优先级

非空闲且没有被挂起的 lane 优先级最高 > 非空闲且被挂起的 lane > 空闲且没有被挂起的 lane > 空闲且被挂起的 lane

  • 对于 InputContinuousLane , 其优先级虽然小于 SyncLane ,但是因为其是连续性的用户输入操作,所以其在处理 SyncLane 的时候一会合并处理 。

  • 为什么返回的是 一个 lanes 而不是一个 lane , 就是因为对于 InputContinuousLane ,其会被合并处理

js
/**
 * 是依据 FiberRoot 和当前正在处理的车道(wipLanes)来确定下一组需要处理的更新车道(Lanes)。
 *  在 React 的并发模式里,更新操作被赋予不同优先级的车道,该函数会综合各类因素挑选出合适的更新车道进行处理。
 * @param {*} root
 * @param {*} wipLanes 当前正在处理的车道
 * @returns
 */
export function getNextLanes(root: FiberRoot, wipLanes: Lanes): Lanes {
  // Early bailout if there's no pending work left.
  // 获取 FiberRootNode 中等待更新的 lane
  const pendingLanes = root.pendingLanes;
  // 如果没有更新的 lane 就直接返回
  if (pendingLanes === NoLanes) {
    return NoLanes;
  }

  let nextLanes = NoLanes;
  // 获取 FiberRootNode 中挂起的 lane(暂停的更新)
  const suspendedLanes = root.suspendedLanes;
  // 获取 FiberRootNode 中 待处理 的 lane
  const pingedLanes = root.pingedLanes;

  // Do not work on any idle work until all the non-idle work has finished,
  // even if the work is suspended.
  // 判断是都存在非空闲的 lane
  const nonIdlePendingLanes = pendingLanes & NonIdleLanes;
  // 1. 如果存在非空闲的 lane
  if (nonIdlePendingLanes !== NoLanes) {
    // 2. 在判断是否存在非空闲的且没有被挂起的 lane
    const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;
    // 如果存在 没有被挂起了 那么就作为高优先级的 lane,下一个处理
    if (nonIdleUnblockedLanes !== NoLanes) {
      // 根据二进制中1的位置来获取最高优先级的 lane (最右边的)
      nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
    } else {
      // 如果不存在被挂起的,那么就将挂起的非空闲的 lane 作为下一个优先级的 lane
      const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;
      if (nonIdlePingedLanes !== NoLanes) {
        nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
      }
    }
  } else {
    // 如果不存在非空闲的 lane,还是一样 先判断是否存在非挂起的,其优先级高于挂起的 lane
    // The only remaining work is Idle.
    const unblockedLanes = pendingLanes & ~suspendedLanes;
    if (unblockedLanes !== NoLanes) {
      nextLanes = getHighestPriorityLanes(unblockedLanes);
    } else {
      if (pingedLanes !== NoLanes) {
        nextLanes = getHighestPriorityLanes(pingedLanes);
      }
    }
  }

  if (nextLanes === NoLanes) {
    // This should only be reachable if we're suspended
    // TODO: Consider warning in this path if a fallback timer is not scheduled.
    return NoLanes;
  }

  // If we're already in the middle of a render, switching lanes will interrupt
  // it and we'll lose our progress. We should only do this if the new lanes are
  // higher priority.
  // 如果我们已经处于渲染过程中,切换车道会中断当前渲染,并且会丢失已经完成的进度。只有当新的车道优先级更高时,我们才应该进行切换
  // 1. 如果当前正在处理的 lane 存在 且 小于待处理的 nextLane 那么才会切换车道
  if (
    wipLanes !== NoLanes &&
    wipLanes !== nextLanes &&
    // If we already suspended with a delay, then interrupting is fine. Don't
    // bother waiting until the root is complete.
    (wipLanes & suspendedLanes) === NoLanes
  ) {
    // 判断待处理的lane的优先级
    const nextLane = getHighestPriorityLane(nextLanes);
    // 当前正在处理的 lane 的优先级
    const wipLane = getHighestPriorityLane(wipLanes);
    if (
      // Tests whether the next lane is equal or lower priority than the wip
      // one. This works because the bits decrease in priority as you go left.
      nextLane >= wipLane ||
      // Default priority updates should not interrupt transition updates. The
      // only difference between default updates and transition updates is that
      // default updates do not support refresh transitions.
      (nextLane === DefaultLane && (wipLane & TransitionLanes) !== NoLanes)
    ) {
      // Keep working on the existing in-progress tree. Do not interrupt.
      return wipLanes;
    }
  }

  if (
    allowConcurrentByDefault &&
    (root.current.mode & ConcurrentUpdatesByDefaultMode) !== NoMode
  ) {
    // Do nothing, use the lanes as they were assigned.
  } else if ((nextLanes & InputContinuousLane) !== NoLanes) {
    // When updates are sync by default, we entangle continuous priority updates
    // and default updates, so they render in the same batch. The only reason
    // they use separate lanes is because continuous updates should interrupt
    // transitions, but default updates should not.
    // 就是当 nextLane 是 SyncLane 且存在 InputContinuousLane 的时候,那么这时候不是只进行 SyncLane 的更新,
    // 而是将 InputContinuousLane 和 SyncLane 都进行更新
    nextLanes |= pendingLanes & DefaultLane;
  }

  const entangledLanes = root.entangledLanes;
  if (entangledLanes !== NoLanes) {
    const entanglements = root.entanglements;
    let lanes = nextLanes & entangledLanes;
    while (lanes > 0) {
      const index = pickArbitraryLaneIndex(lanes);
      const lane = 1 << index;

      nextLanes |= entanglements[index];

      lanes &= ~lane;
    }
  }

  return nextLanes;
}

React 优先级 lane 与 更新

优先级机制