Appearance
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是一个表示单个车道的二进制数。
- 空闲( ~NonIdleLanes )和 非空闲( NonIdleLanes)
- 空闲( ~NonIdleLanes )
表示的其 lane 的标识位为 最左边 3 位的车道事件, 如 IdleLane 、 OffscreenLane, 其优先级很低,不需要及时的去处理
- 非空闲( NonIdleLanes)
表示的其 lane 的标识位为 最右边 29 位的车道事件, 如 DefaultLane 、 DefaultHydrationLane, 其优先级很高,需要及时的去处理
其中有几个比较重要的 如 SyncLane(标识位为 1,优先级最高) , DefaultLane(标识位为 16,优先级中等)
- 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;
}