阅读图解React笔记4
2022 react本文仅仅是阅读 图解 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,简单概括:
- Lane 类型被定义为二进制变量,利用了位掩码的特性,在频繁运算的时候占用内存少,计算速度快。
- Lane 是对于 expirationTime 的重构,使用 expirationTime 表示的字段修改成 lane。
-
使用 Lane 模型相比 expirationTime 的优势:
-
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;
-
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;
-
- Lane 是一个不透明的类型,只在 ReactFiberLane.js 模块维护,其他文件调用需要使用 ReactFiberLane.js 中提供的工具函数。
结论
从 react-reconciler/src/ReactFiberLane.js 中可以得出以下结论:
- 可以使用的比特位一共有 31 位。
- 共定义了 18 种车道变量,每一个变量占有 1 个或多个比特位,分别定义了 Lane 和 Lanes 类型。
- 每一种车道 Lane/Lanes 都有对应的优先级,所以源码中定义了 18 种优先级 LanePriority。
- 占有低位比特位的 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.');
}
}