本文仅仅是阅读 图解 React 原理系列 的笔记,了解更多内容请查看原文链接。

优先级管理

React 内部对于优先级的管理,根据功能的不同分为 LanePriority, SchedulerPriority, ReactPriority 3 种类型。

  • LanePriority 位于 react-reconciler 包
  • SchedulerPriority 位于 scheduler 包
  • ReactPriorityLevel 位于 react-reconciler 包中的 SchedulerWithReactIntegration.js

Lane

Lane (车道模型)的源码位于 react-reconciler/src/ReactFiberLane.js 文件,源码中使用了大量位运算。

Lane 的解释 https://github.com/facebook/react/pull/18796,简单概括:

  1. Lane 类型被定义为二进制变量,利用了位掩码的特性,在频繁运算的时候占用内存少,计算速度快。
  2. Lane 是对于 expirationTime 的重构,使用 expirationTime 表示的字段修改成 lane。
  3. 使用 Lane 模型相比 expirationTime 的优势:

    1. Lane 的任务优先级从批量任务中分离出来,可以更方便判断单个任务与批量任务的优先级是否重叠。

      // 判断单个 task 与 batchOfTasks 的优先级是否重叠
      // 1. 通过 expirationTime 判断
      const isTaskIncludedInBatch = priorityOfTask >= priorityOfBatch;
      // 2. 通过 Lanes 判断
      const isTaskIncludedInBatch = (task & batchOfTasks) !== 0;
      
      // 当同时处理一组任务,该组内有多个任务,且每个任务的优先级不一致
      // 1. 通过 expirationTime 判断,需要维护一个范围
      const isTaskIncludedInBatch =
        taskPriority <= highestPriorityInRage && taskPriority >= lowestPriorityInRange;
      // 2. 通过 Lane 判断
      const isTaskIncludedInBatch = (task & batchOfTasks) !== 0;
      
    2. Lane 使用单个 32 位二进制变量即可代表多个不同的任务,也就是说一个变量即可代表一个组(group),如果要在一个 group 中分离单个 task,非常容易。

      // 通过 expirationTime 实现从 group 增删 task
      // 维护一个链表,按照单个 task 的优先级顺序进行插入
      // 删除单个 task
      task.prev.next = task.next;
      // 增加单个 task
      let current = queue;
      while (task.expirationTime >= current.expirationTime) {
        current = current.next;
      }
      task.next = current.next;
      current.next = task;
      // 比较 task 是否在 group
      const isTaskIncludedInBatch =
        taskPriority <= highestPriorityInRage && taskPriority >= lowestPriorityInRange;
      
      // 通过 Lane 实现
      // 删除单个 task
      batchOfTasks &= ~task;
      // 增加单个 task
      batchOfTasks |= task;
      // 比较 task 是否在 group
      const isTaskIncludedInBatch (task & batchOfTasks) !== 0;
      
  4. Lane 是一个不透明的类型,只在 ReactFiberLane.js 模块维护,其他文件调用需要使用 ReactFiberLane.js 中提供的工具函数。

结论

react-reconciler/src/ReactFiberLane.js 中可以得出以下结论:

  1. 可以使用的比特位一共有 31 位。
  2. 共定义了 18 种车道变量,每一个变量占有 1 个或多个比特位,分别定义了 Lane 和 Lanes 类型。
  3. 每一种车道 Lane/Lanes 都有对应的优先级,所以源码中定义了 18 种优先级 LanePriority。
  4. 占有低位比特位的 Lane 变量对应的优先级越高。
    • 最高优先级为 SyncLanePriority,对应的车道为 SyncLane = 0b0000000000000000000000000000001;
    • 最低优先级位 OffscreenLanePriority,对应的车道为 OffscreenLane = 0b1000000000000000000000000000000;

优先级区别

LanePriority

LanePriority 位于 react-reconciler/src/ReactFiberLane.js 中:

export const SyncLanePriority: LanePriority = 15;
export const SyncBatchedLanePriority: LanePriority = 14;

const InputDiscreteHydrationLanePriority: LanePriority = 13;
export const InputDiscreteLanePriority: LanePriority = 12;

const InputContinuousHydrationLanePriority: LanePriority = 11;
export const InputContinuousLanePriority: LanePriority = 10;

const DefaultHydrationLanePriority: LanePriority = 9;
export const DefaultLanePriority: LanePriority = 8;

const TransitionHydrationPriority: LanePriority = 7;
export const TransitionPriority: LanePriority = 6;

const RetryLanePriority: LanePriority = 5;

const SelectiveHydrationLanePriority: LanePriority = 4;

const IdleHydrationLanePriority: LanePriority = 3;
const IdleLanePriority: LanePriority = 2;

const OffscreenLanePriority: LanePriority = 1;
export const NoLanePriority: LanePriority = 0;

与 fiber 构造过程相关的优先级(如 fiber.updateQueue,fiber.lanes)都使用 LanePriority。

SchedulerPriority

SchedulerPriority 位于 scheduler/src/SchedulerPriorities.js 中:

export type PriorityLevel = 0 | 1 | 2 | 3 | 4 | 5;

export const NoPriority = 0;
export const ImmediatePriority = 1;
export const UserBlockingPriority = 2;
export const NormalPriority = 3;
export const LowPriority = 4;
export const IdlePriority = 5;

与 scheduler 调度中心相关的优先级使用 SchedulerPriority

ReactPriorityLevel

ReactPriorityLevel 位于 react-reconciler/src/SchedulerWithReactIntegration.js 中:

export const ImmediatePriority: ReactPriorityLevel = 99;
export const UserBlockingPriority: ReactPriorityLevel = 98;
export const NormalPriority: ReactPriorityLevel = 97;
export const LowPriority: ReactPriorityLevel = 96;
export const IdlePriority: ReactPriorityLevel = 95;
export const NoPriority: ReactPriorityLevel = 90;

LanePriority 与 SchedulerPriority 通过 ReactPriorityLevel 进行转换。

转换

为了协同调度中心和 fiber 树构造中对优先级的使用,需要转换 SchedulerPriority 和 LanePriority,转换的桥梁是 ReactPriorityLevel。

react-reconciler/src/ReactFiberLane.js 中,可以互转 LanePriority 和 ReactPriorityLevel

// LanePriority => ReactPriorityLevel
export function lanePriorityToSchedulerPriority(lanePriority: LanePriority): ReactPriorityLevel {
  switch (lanePriority) {
    case SyncLanePriority:
    case SyncBatchedLanePriority:
      return ImmediateSchedulerPriority;
    case InputDiscreteHydrationLanePriority:
    case InputDiscreteLanePriority:
    case InputContinuousHydrationLanePriority:
    case InputContinuousLanePriority:
      return UserBlockingSchedulerPriority;
    case DefaultHydrationLanePriority:
    case DefaultLanePriority:
    case TransitionHydrationPriority:
    case TransitionPriority:
    case SelectiveHydrationLanePriority:
    case RetryLanePriority:
      return NormalSchedulerPriority;
    case IdleHydrationLanePriority:
    case IdleLanePriority:
    case OffscreenLanePriority:
      return IdleSchedulerPriority;
    case NoLanePriority:
      return NoSchedulerPriority;
    default:
      invariant(false, 'Invalid update priority: %s. This is a bug in React.', lanePriority);
  }
}

// ReactPriorityLevel => LanePriority
export function schedulerPriorityToLanePriority(
  schedulerPriorityLevel: ReactPriorityLevel
): LanePriority {
  switch (schedulerPriorityLevel) {
    case ImmediateSchedulerPriority:
      return SyncLanePriority;
    case UserBlockingSchedulerPriority:
      return InputContinuousLanePriority;
    case NormalSchedulerPriority:
    case LowSchedulerPriority:
      return DefaultLanePriority;
    case IdleSchedulerPriority:
      return IdleLanePriority;
    default:
      return NoLanePriority;
  }
}

react-reconciler/src/SchedulerWithReactIntegration.js 中,可以互转 SchedulerPriority 和 ReactPriorityLevel

// SchedulerPriority => ReactPriorityLevel
export function getCurrentPriorityLevel(): ReactPriorityLevel {
  switch (Scheduler_getCurrentPriorityLevel()) {
    case Scheduler_ImmediatePriority:
      return ImmediatePriority;
    case Scheduler_UserBlockingPriority:
      return UserBlockingPriority;
    case Scheduler_NormalPriority:
      return NormalPriority;
    case Scheduler_LowPriority:
      return LowPriority;
    case Scheduler_IdlePriority:
      return IdlePriority;
    default:
      invariant(false, 'Unknown priority level.');
  }
}

// ReactPriorityLevel => SchedulerPriority
function reactPriorityToSchedulerPriority(reactPriorityLevel) {
  switch (reactPriorityLevel) {
    case ImmediatePriority:
      return Scheduler_ImmediatePriority;
    case UserBlockingPriority:
      return Scheduler_UserBlockingPriority;
    case NormalPriority:
      return Scheduler_NormalPriority;
    case LowPriority:
      return Scheduler_LowPriority;
    case IdlePriority:
      return Scheduler_IdlePriority;
    default:
      invariant(false, 'Unknown priority level.');
  }
}

参考链接