# 阅读图解React笔记4
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,简单概括:
-
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 的优先级顺序进行插入// 删除单个 tasktask.prev.next = task.next;// 增加单个 tasklet current = queue;while (task.expirationTime >= current.expirationTime) {current = current.next;}task.next = current.next;current.next = task;// 比较 task 是否在 groupconst isTaskIncludedInBatch =taskPriority <= highestPriorityInRage && taskPriority >= lowestPriorityInRange;// 通过 Lane 实现// 删除单个 taskbatchOfTasks &= ~task;// 增加单个 taskbatchOfTasks |= task;// 比较 task 是否在 groupconst 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 => ReactPriorityLevelexport 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 => LanePriorityexport 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 => ReactPriorityLevelexport 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 => SchedulerPriorityfunction 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.'); }}