# 阅读图解React笔记4

6 min read
Table of Contents

本文仅仅是阅读 图解 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.');
}
}

参考链接


More Posts