ReactFiberLane (React Fiber 优先级管理)
ReactFiberLane 模块是 React Fiber 架构中处理 更新优先级的核心部分 ,负责管理不同更新任务的优先级调度。
一、作用
- Lane (车道) :一个位掩码( bitmask ),代表一种优先级级别。多个 lane 可以组合成一个 Lanes ,表示一组优先级
- Lanes : 类型通常为
number,每一位代表一个 lane 是否被设置 - 不同类型的更新(如用户交互、数据获取、过渡等)会被分配不同优先级的 lane
1.核心作用
- 优先级系统 : 使用 Lane 模型(32 位二进制数)表示不同的优先级
- 调度优化 : 高优先级更新( 如用户输入 ) 可中断低优先级渲染,实现流程交互
2.工程流程
- 发起更新 : 组件调用
setState()➞ 分配优先级 ➞ 标记 Fiber 节点 - 调度决策 : 调度器检查 Fiber 的 lanes ,选择最高优先级任务执行
- 中断和恢复 : 若高优先级更新(如点击事件)到来,当前低优先级渲染被中断,资源让给新任务
3.设计优势
- 高效位运算 : 使用
bitmask操作 (|、&、~) 实现O(N)复杂度的优先级管理 - 并行处理能力 : 支持同时调度多个优先级任务
- 兼容性分层 : 旧优先级系统 ( lane ) 和新系统 ( lanes ) 共存,平滑过渡
二、部分常数
开发者寄语
- 以下优先级值应与
getLabelForLane()保持同步,该函数由react-devtools-timeline使用。 - 如果这些值被更改,该包应重新构建并重新部署。
// Lane values below should be kept in sync with getLabelForLane(), used by react-devtools-timeline.
// If those values are changed that package should be rebuilt and redeployed.
// 总共多少个优先级
export const TotalLanes = 31;
// 无优先级组
export const NoLanes: Lanes = 0b0000000000000000000000000000000;
// 无优先级
export const NoLane: Lane = 0b0000000000000000000000000000000;
// 同步水合优先级
export const SyncHydrationLane: Lane = 0b0000000000000000000000000000001;
// 同步优先级
export const SyncLane: Lane = 0b0000000000000000000000000000010;
// 同步优先级索引
export const SyncLaneIndex: number = 1;
// 输入连续水合优先级
export const InputContinuousHydrationLane: Lane = 0b0000000000000000000000000000100;
// 连续输入优先级
export const InputContinuousLane: Lane = 0b0000000000000000000000000001000;
// 默认水合优先级
export const DefaultHydrationLane: Lane = 0b0000000000000000000000000010000;
// 默认优先级
export const DefaultLane: Lane = 0b0000000000000000000000000100000;
// 同步更新优先级
export const SyncUpdateLanes: Lane =
SyncLane | InputContinuousLane | DefaultLane;
// 手势(交互)优先级
export const GestureLane: Lane = 0b0000000000000000000000001000000;
// 过渡水合优先级
const TransitionHydrationLane: Lane = 0b0000000000000000000000010000000;
// 过渡优先级
const TransitionLanes: Lanes = 0b0000000001111111111111100000000;
// 过渡优先级 - 1
const TransitionLane1: Lane = 0b0000000000000000000000100000000;
// 过渡优先级 - 2
const TransitionLane2: Lane = 0b0000000000000000000001000000000;
// 过渡优先级 - 3
const TransitionLane3: Lane = 0b0000000000000000000010000000000;
// 过渡优先级 - 4
const TransitionLane4: Lane = 0b0000000000000000000100000000000;
// 过渡优先级 - 5
const TransitionLane5: Lane = 0b0000000000000000001000000000000;
// 过渡优先级 - 6
const TransitionLane6: Lane = 0b0000000000000000010000000000000;
// 过渡优先级 - 7
const TransitionLane7: Lane = 0b0000000000000000100000000000000;
// 过渡优先级 - 8
const TransitionLane8: Lane = 0b0000000000000001000000000000000;
// 过渡优先级 - 9
const TransitionLane9: Lane = 0b0000000000000010000000000000000;
// 过渡优先级 - 10
const TransitionLane10: Lane = 0b0000000000000100000000000000000;
// 过渡优先级 - 11
const TransitionLane11: Lane = 0b0000000000001000000000000000000;
// 过渡优先级 - 12
const TransitionLane12: Lane = 0b0000000000010000000000000000000;
// 过渡优先级 - 13
const TransitionLane13: Lane = 0b0000000000100000000000000000000;
// 过渡优先级 - 14
const TransitionLane14: Lane = 0b0000000001000000000000000000000;
// 一些过渡的优先级
export const SomeTransitionLane: Lane = TransitionLane1;
// 过渡更新优先级组
const TransitionUpdateLanes =
TransitionLane1 |
TransitionLane2 |
TransitionLane3 |
TransitionLane4 |
TransitionLane5 |
TransitionLane6 |
TransitionLane7 |
TransitionLane8 |
TransitionLane9 |
TransitionLane10;
// 过渡延迟优先级
const TransitionDeferredLanes =
TransitionLane11 | TransitionLane12 | TransitionLane13 | TransitionLane14;
// 再次尝试优先级
const RetryLanes: Lanes = 0b0000011110000000000000000000000;
// 再次尝试优先级 - 1
const RetryLane1: Lane = 0b0000000010000000000000000000000;
// 再次尝试优先级 - 2
const RetryLane2: Lane = 0b0000000100000000000000000000000;
// 再次尝试优先级 - 3
const RetryLane3: Lane = 0b0000001000000000000000000000000;
// 再次尝试优先级 - 4
const RetryLane4: Lane = 0b0000010000000000000000000000000;
// 一些尝试优先级
export const SomeRetryLane: Lane = RetryLane1;
// 选择性水合优先级
export const SelectiveHydrationLane: Lane = 0b0000100000000000000000000000000;
// 非闲置优先级
const NonIdleLanes: Lanes = 0b0000111111111111111111111111111;
// 闲置水合优先级
export const IdleHydrationLane: Lane = 0b0001000000000000000000000000000;
// 闲置优先级
export const IdleLane: Lane = 0b0010000000000000000000000000000;
// 可视区域外优先级
export const OffscreenLane: Lane = 0b0100000000000000000000000000000;
// 延迟有现金
export const DeferredLane: Lane = 0b1000000000000000000000000000000;
// Any lane that might schedule an update. This is used to detect infinite
// update loops, so it doesn't include hydration lanes or retries.
// 任何可能安排更新的优先级。这用于检测无限更新循环,因此不包括 hydration 优先级或重试。
// 更新优先级组
export const UpdateLanes: Lanes =
SyncLane | InputContinuousLane | DefaultLane | TransitionUpdateLanes;
// 水合优先级组
export const HydrationLanes =
SyncHydrationLane |
InputContinuousHydrationLane |
DefaultHydrationLane |
TransitionHydrationLane |
SelectiveHydrationLane |
IdleHydrationLane;
三、获取优先级标签
// This function is used for the experimental timeline (react-devtools-timeline)
// It should be kept in sync with the Lanes values above.
// 这个函数用于实验时间线(react-devtools-timeline)
// 它应与上面的 Lanes 值保持同步。
export function getLabelForLane(lane: Lane): string | void {
if (enableSchedulingProfiler) {
if (lane & SyncHydrationLane) {
return 'SyncHydrationLane';
}
if (lane & SyncLane) {
return 'Sync';
}
if (lane & InputContinuousHydrationLane) {
return 'InputContinuousHydration';
}
if (lane & InputContinuousLane) {
return 'InputContinuous';
}
if (lane & DefaultHydrationLane) {
return 'DefaultHydration';
}
if (lane & DefaultLane) {
return 'Default';
}
if (lane & TransitionHydrationLane) {
return 'TransitionHydration';
}
if (lane & TransitionLanes) {
return 'Transition';
}
if (lane & RetryLanes) {
return 'Retry';
}
if (lane & SelectiveHydrationLane) {
return 'SelectiveHydration';
}
if (lane & IdleHydrationLane) {
return 'IdleHydration';
}
if (lane & IdleLane) {
return 'Idle';
}
if (lane & OffscreenLane) {
return 'Offscreen';
}
if (lane & DeferredLane) {
return 'Deferred';
}
}
}
四、无时间戳
export const NoTimestamp = -1;
五、获取下一个优先级组
export function getNextLanes(
root: FiberRoot,
wipLanes: Lanes,
rootHasPendingCommit: boolean,
): Lanes {
// Early bailout if there's no pending work left.
// 如果没有待处理的工作,则提前退出。
const pendingLanes = root.pendingLanes;
if (pendingLanes === NoLanes) {
return NoLanes;
}
let nextLanes: Lanes = NoLanes;
const suspendedLanes = root.suspendedLanes;
const pingedLanes = root.pingedLanes;
const warmLanes = root.warmLanes;
// finishedLanes represents a completed tree that is ready to commit.
//
// It's not worth doing discarding the completed tree in favor of performing
// speculative work. So always check this before deciding to warm up
// the siblings.
//
// Note that this is not set in a "suspend indefinitely" scenario, like when
// suspending outside of a Suspense boundary, or in the shell during a
// transition — only in cases where we are very likely to commit the tree in
// a brief amount of time (i.e. below the "Just Noticeable Difference"
// threshold).
//
// Do not work on any idle work until all the non-idle work has finished,
// even if the work is suspended.
// finishedLanes 表示一个已完成的树,可以提交。
//
// 为了执行推测性工作而丢弃已完成的树是不值得的。
// 因此,在决定预先处理兄弟节点之前,请务必检查这一点。
//
// 注意,这种情况不会在“无限期挂起”中设置,例如在 Suspense 边界之外挂起
// ,或在过渡期间的 shell 中,只有在我们非常可能在短时间内提交树的情况下
// 才会设置(即低于“刚可察觉差异”阈值)。
// 在所有非空闲工作完成之前,不要处理任何空闲工作,
// 即使该工作已被挂起。
const nonIdlePendingLanes = pendingLanes & NonIdleLanes;
if (nonIdlePendingLanes !== NoLanes) {
// First check for fresh updates.
// 首先检查最新更新。
const nonIdleUnblockedLanes = nonIdlePendingLanes & ~suspendedLanes;
if (nonIdleUnblockedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(nonIdleUnblockedLanes);
} else {
// No fresh updates. Check if suspended work has been pinged.
// 没有新的更新。检查是否已提醒暂停的工作。
const nonIdlePingedLanes = nonIdlePendingLanes & pingedLanes;
if (nonIdlePingedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(nonIdlePingedLanes);
} else {
// Nothing has been pinged. Check for lanes that need to be prewarmed.
// 没有任何内容被 ping。请检查需要预热的 Lane
if (!rootHasPendingCommit) {
const lanesToPrewarm = nonIdlePendingLanes & ~warmLanes;
if (lanesToPrewarm !== NoLanes) {
nextLanes = getHighestPriorityLanes(lanesToPrewarm);
}
}
}
}
} else {
// The only remaining work is Idle.
// TODO: Idle isn't really used anywhere, and the thinking around
// speculative rendering has evolved since this was implemented. Consider
// removing until we've thought about this again.
// First check for fresh updates.
// 唯一剩下的工作是空闲。
// 待办事项:空闲实际上并没有在任何地方使用,而且关于
// 推测渲染的思路自实现以来已经发展。可以考虑在我们
// 重新考虑之前先删除它。
// 首先检查是否有新的更新。
const unblockedLanes = pendingLanes & ~suspendedLanes;
if (unblockedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(unblockedLanes);
} else {
// No fresh updates. Check if suspended work has been pinged.
// 没有新的更新。检查是否已提醒暂停的工作。
if (pingedLanes !== NoLanes) {
nextLanes = getHighestPriorityLanes(pingedLanes);
} else {
// Nothing has been pinged. Check for lanes that need to be prewarmed.
// 没有任何内容被 ping。请检查是否有需要预热的 Lane
if (!rootHasPendingCommit) {
const lanesToPrewarm = pendingLanes & ~warmLanes;
if (lanesToPrewarm !== NoLanes) {
nextLanes = getHighestPriorityLanes(lanesToPrewarm);
}
}
}
}
}
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.
// 只有在我们被暂停时才应该到达此处
// TODO: 如果未安排备用计时器,考虑在此路径中发出警告。
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.
// 如果我们已经在渲染过程中,切换 Lane 会中断渲染
// 并且我们会丢失进度。我们应该仅在新 Lane 优先级更高时才这样做。
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
) {
const nextLane = getHighestPriorityLane(nextLanes);
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.
// 测试下一个 Lane 是否与正在进行的 Lane 优先级相同或更低
// 这是可行的,因为从左向右位的优先级会降低。
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;
}
}
return nextLanes;
}
六、获取下一个要同步刷新的小节
export function getNextLanesToFlushSync(
root: FiberRoot,
extraLanesToForceSync: Lane | Lanes,
): Lanes {
// Similar to getNextLanes, except instead of choosing the next lanes to work
// on based on their priority, it selects all the lanes that have equal or
// higher priority than those are given. That way they can be synchronously
// rendered in a single batch.
//
// The main use case is updates scheduled by popstate events, which are
// flushed synchronously even though they are transitions.
// Note that we intentionally treat this as a sync flush to include any
// sync updates in a single pass but also intentionally disables View Transitions
// inside popstate. Because they can start synchronously before scroll restoration
// happens.
// 类似于 getNextLanes,不同之处在于它不是根据优先级选择要处理的下一个 lane,
// 而是选择所有优先级等于或高于给定值的 lane。这样它们可以在一个批次中同步渲染。
// 主要的使用场景是由 popstate 事件调度的更新,这些更新即使是过渡更新,也会同步刷新。
// 请注意,我们有意将其视为同步刷新,以便在一次操作中包含所有同步更新,
// 但也有意在 popstate 中禁用视图过渡。因为它们可以在滚动恢复发生之前同步启动。
const lanesToFlush = SyncUpdateLanes | extraLanesToForceSync;
// Early bailout if there's no pending work left.
// 如果没有待处理的工作,则提前退出。
const pendingLanes = root.pendingLanes;
if (pendingLanes === NoLanes) {
return NoLanes;
}
const suspendedLanes = root.suspendedLanes;
const pingedLanes = root.pingedLanes;
// Remove lanes that are suspended (but not pinged)
// 移除已挂起的 Lane(但未 ping)
const unblockedLanes = pendingLanes & ~(suspendedLanes & ~pingedLanes);
const unblockedLanesWithMatchingPriority =
unblockedLanes & getLanesOfEqualOrHigherPriority(lanesToFlush);
// If there are matching hydration lanes, we should do those by themselves.
// Hydration lanes must never include updates.
// 如果有匹配的 hydration 线路,我们应该单独处理这些线路。
// Hydration 线路绝不能包含更新。
if (unblockedLanesWithMatchingPriority & HydrationLanes) {
return (
(unblockedLanesWithMatchingPriority & HydrationLanes) | SyncHydrationLane
);
}
if (unblockedLanesWithMatchingPriority) {
// Always include the SyncLane as part of the result, even if there's no
// pending sync work, to indicate the priority of the entire batch of work
// is considered Sync.
// 始终将 SyncLane 包含在结果中,即使没有挂起的同步工作,
// 也要表明整个工作批次的优先级被认为是同步的。
return unblockedLanesWithMatchingPriority | SyncLane;
}
return NoLanes;
}
七、检查根是否正在预渲染
export function checkIfRootIsPrerendering(
root: FiberRoot,
renderLanes: Lanes,
): boolean {
const pendingLanes = root.pendingLanes;
const suspendedLanes = root.suspendedLanes;
const pingedLanes = root.pingedLanes;
// Remove lanes that are suspended (but not pinged)
// 移除已挂起的 Lane(但未 ping 的)
const unblockedLanes = pendingLanes & ~(suspendedLanes & ~pingedLanes);
// If there are no unsuspended or pinged lanes, that implies that we're
// performing a prerender.
// 如果没有未暂停或被 ping 的 Lane ,这意味着我们正在执行预渲染。
return (unblockedLanes & renderLanes) === 0;
}
八、 获取纠缠的优先级组
export function getEntangledLanes(root: FiberRoot, renderLanes: Lanes): Lanes {
let entangledLanes = renderLanes;
if ((entangledLanes & 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.
// 当更新默认同步时,我们会将连续优先级更新和默认更新纠缠在一起,
// 因此它们在同一批次中渲染。
// 它们使用不同 Lane 的唯一原因是连续更新应当中断过渡,
// 而默认更新不应中断过渡。
entangledLanes |= entangledLanes & DefaultLane;
}
// Check for entangled lanes and add them to the batch.
//
// A lane is said to be entangled with another when it's not allowed to render
// in a batch that does not also include the other lane. Typically we do this
// when multiple updates have the same source, and we only want to respond to
// the most recent event from that source.
//
// 检查纠缠的 Lane 并将它们添加到批处理中。
//
// 当一个 Lane 不允许在不包含另一个 Lane 的批处理中渲染时,称该 Lane 与另一个 Lane 纠缠在一起。
// 通常我们在多个更新具有相同来源时这样做,并且我们只想响应来自该来源的最新事件。
//
// Note that we apply entanglements *after* checking for partial work above.
// This means that if a lane is entangled during an interleaved event while
// it's already rendering, we won't interrupt it. This is intentional, since
// entanglement is usually "best effort": we'll try our best to render the
// lanes in the same batch, but it's not worth throwing out partially
// completed work in order to do it.
//
// 请注意,我们在上面检查部分工作之后**才**应用纠缠。
// 这意味着如果某条 Lane 在交错事件期间被纠缠,而它已经在渲染中,
// 我们不会打断它。这是有意为之,因为纠缠通常是“尽力而为”:
// 我们会尽力在同一批次中渲染这些 Lane ,但为了强行做到这一点而放弃部分完成的工作是不值得的。
//
// TODO: Reconsider this. The counter-argument is that the partial work
// represents an intermediate state, which we don't want to show to the user.
// And by spending extra time finishing it, we're increasing the amount of
// time it takes to show the final state, which is what they are actually
// waiting for.
// 待办事项:重新考虑这一点。反驳意见是,部分工作代表的是一个中间状态,
// 而我们不希望向用户展示这个状态。通过花额外的时间完成它,我们实际上是在增加显示
// 最终状态所需的时间,而这才是用户真正等待的。
//
// For those exceptions where entanglement is semantically important,
// we should ensure that there is no partial work at the
// time we apply the entanglement.
//
// 对于那些纠缠在语义上很重要的例外情况,我们应该确保在应用纠缠时不存在任何部分完成的工作。
const allEntangledLanes = root.entangledLanes;
if (allEntangledLanes !== NoLanes) {
const entanglements = root.entanglements;
let lanes = entangledLanes & allEntangledLanes;
while (lanes > 0) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
entangledLanes |= entanglements[index];
lanes &= ~lane;
}
}
return entangledLanes;
}
九、标记着急执行为过期
export function markStarvedLanesAsExpired(
root: FiberRoot,
currentTime: number,
): void {
// TODO: This gets called every time we yield. We can optimize by storing
// the earliest expiration time on the root. Then use that to quickly bail out
// of this function.
//
// TODO:每次我们让出时都会调用这个函数。
// 我们可以通过在根节点上存储最早的过期时间来进行优化,然后使用它快速退出这个函数。
const pendingLanes = root.pendingLanes;
const suspendedLanes = root.suspendedLanes;
const pingedLanes = root.pingedLanes;
const expirationTimes = root.expirationTimes;
// Iterate through the pending lanes and check if we've reached their
// expiration time. If so, we'll assume the update is being starved and mark
// it as expired to force it to finish.
// TODO: We should be able to replace this with upgradePendingLanesToSync
// 遍历待处理的 Lane 并检查是否已达到它们的过期时间。
// 如果是,我们将假设更新被饿死并将其标记为过期,以强制它完成。
// TODO: 我们应该能够用 upgradePendingLanesToSync 替换它
//
// We exclude retry lanes because those must always be time sliced, in order
// to unwrap uncached promises.
// TODO: Write a test for this
// 我们排除重试 Lane ,因为这些 Lane 必须始终进行时间切片,以便展开未缓存的 Promise。
// TODO: 为此编写一个测试
let lanes = enableRetryLaneExpiration
? pendingLanes
: pendingLanes & ~RetryLanes;
while (lanes > 0) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
const expirationTime = expirationTimes[index];
if (expirationTime === NoTimestamp) {
// Found a pending lane with no expiration time. If it's not suspended, or
// if it's pinged, assume it's CPU-bound. Compute a new expiration time
// using the current time.
//
// 发现了一个没有过期时间的待处理 Lane 。如果它没有被挂起,或者如果它被 ping 过,
// 则假设它是 CPU 限制的。使用当前时间计算一个新的过期时间。
if (
(lane & suspendedLanes) === NoLanes ||
(lane & pingedLanes) !== NoLanes
) {
// Assumes timestamps are monotonically increasing.
// 假设时间戳是单调递增的。
expirationTimes[index] = computeExpirationTime(lane, currentTime);
}
} else if (expirationTime <= currentTime) {
// This lane expired
// 此 Lane 已过期
root.expiredLanes |= lane;
}
lanes &= ~lane;
}
}
十、获取最高优先级待处理 Lane
// This returns the highest priority pending lanes regardless of whether they
// are suspended.
// 这会返回优先级最高的待处理 Lane ,无论它们是否被挂起。
export function getHighestPriorityPendingLanes(root: FiberRoot): Lanes {
return getHighestPriorityLanes(root.pendingLanes);
}
十一、获取出错时需要同步重试的 Lane
export function getLanesToRetrySynchronouslyOnError(
root: FiberRoot,
originallyAttemptedLanes: Lanes,
): Lanes {
if (root.errorRecoveryDisabledLanes & originallyAttemptedLanes) {
// The error recovery mechanism is disabled until these lanes are cleared.
// 在这些 Lane 被清除之前,错误恢复机制已被禁用。
return NoLanes;
}
const everythingButOffscreen = root.pendingLanes & ~OffscreenLane;
if (everythingButOffscreen !== NoLanes) {
return everythingButOffscreen;
}
if (everythingButOffscreen & OffscreenLane) {
return OffscreenLane;
}
return NoLanes;
}
十二、 Lane 包含判定
1.包含同步 Lane
export function includesSyncLane(lanes: Lanes): boolean {
return (lanes & (SyncLane | SyncHydrationLane)) !== NoLanes;
}
2.包含非闲置工作
export function includesNonIdleWork(lanes: Lanes): boolean {
return (lanes & NonIdleLanes) !== NoLanes;
}
3.仅包括重试
export function includesOnlyRetries(lanes: Lanes): boolean {
return (lanes & RetryLanes) === lanes;
}
4.仅包括非紧急 Lane
export function includesOnlyNonUrgentLanes(lanes: Lanes): boolean {
// TODO: Should hydration lanes be included here? This function is only
// used in `updateDeferredValueImpl`.
// 待办事项:这里应该包含 hydration lanes 吗?
// 这个函数只在 `updateDeferredValueImpl` 中使用。
const UrgentLanes = SyncLane | InputContinuousLane | DefaultLane;
return (lanes & UrgentLanes) === NoLanes;
}
5.仅包含过渡
export function includesOnlyTransitions(lanes: Lanes): boolean {
return (lanes & TransitionLanes) === lanes;
}
6.包含过渡 Lane
export function includesTransitionLane(lanes: Lanes): boolean {
return (lanes & TransitionLanes) !== NoLanes;
}
7.包含重试 Lane
export function includesRetryLane(lanes: Lanes): boolean {
return (lanes & RetryLanes) !== NoLanes;
}
8.包含闲置组 Lane
export function includesIdleGroupLanes(lanes: Lanes): boolean {
return (
(lanes &
(SelectiveHydrationLane |
IdleHydrationLane |
IdleLane |
OffscreenLane |
DeferredLane)) !==
NoLanes
);
}
9. 仅包含水合 Lane
export function includesOnlyHydrationLanes(lanes: Lanes): boolean {
return (lanes & HydrationLanes) === lanes;
}
10.仅包含视图外 Lane
export function includesOnlyOffscreenLanes(lanes: Lanes): boolean {
return (lanes & OffscreenLane) === lanes;
}
11.仅包含视图外或者水合 Lane
export function includesOnlyHydrationOrOffscreenLanes(lanes: Lanes): boolean {
return (lanes & (HydrationLanes | OffscreenLane)) === lanes;
}
12.仅包括可进行视图转换的 Lane
export function includesOnlySuspenseyCommitEligibleLanes(
lanes: Lanes,
): boolean {
return (
(lanes & (TransitionLanes | RetryLanes | IdleLane | GestureLane)) === lanes
);
}
13.包含加载指示器 Lane
export function includesLoadingIndicatorLanes(lanes: Lanes): boolean {
return (lanes & (SyncLane | DefaultLane)) !== NoLanes;
}
14.包含阻塞 Lane
export function includesBlockingLane(lanes: Lanes): boolean {
const SyncDefaultLanes =
SyncHydrationLane |
SyncLane |
InputContinuousHydrationLane |
InputContinuousLane |
DefaultHydrationLane |
DefaultLane |
GestureLane;
return (lanes & SyncDefaultLanes) !== NoLanes;
}
15.包含已过期 Lane
export function includesExpiredLane(root: FiberRoot, lanes: Lanes): boolean {
// This is a separate check from includesBlockingLane because a lane can
// expire after a render has already started.
// 这是与 includesBlockingLane 分开的一项检查,
// 因为一个 Lane 可能在渲染已经开始之后过期。
return (lanes & root.expiredLanes) !== NoLanes;
}
十三、 Lane 类别判定
1.阻塞 Lane 判定
export function isBlockingLane(lane: Lane): boolean {
const SyncDefaultLanes =
SyncHydrationLane |
SyncLane |
InputContinuousHydrationLane |
InputContinuousLane |
DefaultHydrationLane |
DefaultLane |
GestureLane;
return (lane & SyncDefaultLanes) !== NoLanes;
}
2.过渡 Lane 判定
export function isTransitionLane(lane: Lane): boolean {
return (lane & TransitionLanes) !== NoLanes;
}
3.手势渲染判定
export function isGestureRender(lanes: Lanes): boolean {
if (!enableGestureTransition) {
return false;
}
// This should render only the one lane.
// 这应该只显示一个 Lane 。
return lanes === GestureLane;
}
十四、请求下一个过渡更新 Lane
export function claimNextTransitionUpdateLane(): Lane {
// Cycle through the lanes, assigning each new transition to the next lane.
// In most cases, this means every transition gets its own lane, until we
// run out of lanes and cycle back to the beginning.
//
// 遍历各条 Lane ,将每个新的过渡分配到下一个 Lane 。
// 在大多数情况下,这意味着每个过渡都会有自己的 Lane ,直到
// 用完所有 Lane 后再从头开始循环。
const lane = nextTransitionUpdateLane;
nextTransitionUpdateLane <<= 1;
if ((nextTransitionUpdateLane & TransitionUpdateLanes) === NoLanes) {
nextTransitionUpdateLane = TransitionLane1;
}
return lane;
}
十五、请求下一个过渡延迟 Lane
export function claimNextRetryLane(): Lane {
const lane = nextRetryLane;
nextRetryLane <<= 1;
if ((nextRetryLane & RetryLanes) === NoLanes) {
nextRetryLane = RetryLane1;
}
return lane;
}
十六、获取最高优先级 Lane
export function getHighestPriorityLane(lanes: Lanes): Lane {
return lanes & -lanes;
}
十七、获取相同或更高优先级的 Lane
function getLanesOfEqualOrHigherPriority(lanes: Lane | Lanes): Lanes {
// Create a mask with all bits to the right or same as the highest bit.
// So if lanes is 0b100, the result would be 0b111.
// If lanes is 0b101, the result would be 0b111.
//
// 创建一个掩码,包含最高位及其右边的所有位。
// 所以如果 lanes 是 0b100,结果就是 0b111。
// 如果 lanes 是 0b101,结果就是 0b111。
const lowestPriorityLaneIndex = 31 - clz32(lanes);
return (1 << (lowestPriorityLaneIndex + 1)) - 1;
}
十八、选择任意 Lane
export function pickArbitraryLane(lanes: Lanes): Lane {
// This wrapper function gets inlined. Only exists so to communicate that it
// doesn't matter which bit is selected; you can pick any bit without
// affecting the algorithms where its used. Here I'm using
// getHighestPriorityLane because it requires the fewest operations.
//
// 这个包装函数会被内联。它存在的唯一目的是为了说明无论选择哪一位都无关紧要;
// 你可以选择任意一位而不会影响使用该函数的算法。
// 在这里我使用 getHighestPriorityLane,因为它所需的操作最少。
return getHighestPriorityLane(lanes);
}
十九、是否包含部分索引
export function includesSomeLane(a: Lanes | Lane, b: Lanes | Lane): boolean {
return (a & b) !== NoLanes;
}
二十、是否为 Lane 子集
export function isSubsetOfLanes(set: Lanes, subset: Lanes | Lane): boolean {
return (set & subset) === subset;
}
廿一、合并 Lane
export function mergeLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes {
return a | b;
}
廿二、移除 Lane
export function removeLanes(set: Lanes, subset: Lanes | Lane): Lanes {
return set & ~subset;
}
廿三、交叉 Lane
export function intersectLanes(a: Lanes | Lane, b: Lanes | Lane): Lanes {
return a & b;
}
廿四、 Lane 到 Lanes
// Seems redundant, but it changes the type from a single lane (used for
// updates) to a group of lanes (used for flushing work).
// 看起来多余,但它将类型从单条 Lane (用于更新)改变为 Lane 组(用于刷新工作)。
export function laneToLanes(lane: Lane): Lanes {
return lane as Lanes;
}
廿五、优先 Lane
export function higherPriorityLane(a: Lane, b: Lane): Lane {
// This works because the bit ranges decrease in priority as you go left.
// 这是可行的,因为比特范围从左到右优先级逐渐降低。
return a !== NoLane && a < b ? a : b;
}
廿六、创建 Lane 图
export function createLaneMap<T>(initial: T): LaneMap<T> {
// Intentionally pushing one by one.
// 有意一个一个推
// https://v8.dev/blog/elements-kinds#avoid-creating-holes
const laneMap = [];
for (let i = 0; i < TotalLanes; i++) {
laneMap.push(initial);
}
return laneMap;
}
廿七、根节点相关
1.标记根已更新
export function markRootUpdated(root: FiberRoot, updateLane: Lane) {
root.pendingLanes |= updateLane;
if (enableDefaultTransitionIndicator) {
// Mark that this lane might need a loading indicator to be shown.
// 标记这个 Lane 可能需要显示加载指示器。
root.indicatorLanes |= updateLane & TransitionLanes;
}
// If there are any suspended transitions, it's possible this new update
// could unblock them. Clear the suspended lanes so that we can try rendering
// them again.
//
// 如果有任何挂起的过渡,这个新更新可能会解除它们的阻塞。
// 清除挂起的 Lane ,以便我们可以再次尝试渲染它们。
//
// TODO: We really only need to unsuspend only lanes that are in the
// `subtreeLanes` of the updated fiber, or the update lanes of the return
// path. This would exclude suspended updates in an unrelated sibling tree,
// since there's no way for this update to unblock it.
//
// TODO: 我们实际上只需要恢复那些在更新 fiber 的 `subtreeLanes` 中的 lanes,
// 或者返回路径的更新 lanes。这将排除不相关兄弟树中的挂起更新,
// 因为没有办法让该更新解除它的阻塞。
//
// We don't do this if the incoming update is idle, because we never process
// idle updates until after all the regular updates have finished; there's no
// way it could unblock a transition.
//
// 如果传入的更新是空闲的,我们不会这样做,因为我们从不处理
// 空闲更新,直到所有常规更新完成之后;它不可能解除过渡阻塞。
if (updateLane !== IdleLane) {
root.suspendedLanes = NoLanes;
root.pingedLanes = NoLanes;
root.warmLanes = NoLanes;
}
}
2.标记根已暂停
export function markRootSuspended(
root: FiberRoot,
suspendedLanes: Lanes,
spawnedLane: Lane,
didAttemptEntireTree: boolean,
) {
// TODO: Split this into separate functions for marking the root at the end of
// a render attempt versus suspending while the root is still in progress.
//
// 待办事项:将此拆分成单独的函数,用于标记渲染尝试结束时的根节点,
// 或在根节点仍在进行时暂停。
root.suspendedLanes |= suspendedLanes;
root.pingedLanes &= ~suspendedLanes;
if (didAttemptEntireTree) {
// Mark these lanes as warm so we know there's nothing else to work on.
// 将这些 Lane 标记为已处理,以便我们知道没有其他工作需要进行。
root.warmLanes |= suspendedLanes;
} else {
// Render unwound without attempting all the siblings. Do no mark the lanes
// as warm. This will cause a prewarm render to be scheduled.
//
// 渲染未展开的部分,而不尝试所有兄弟节点。不要将 Lane 标记为预热。
// 这将导致预热渲染被安排。
}
// The suspended lanes are no longer CPU-bound. Clear their expiration times.
// 被挂起的线程不再受 CPU 限制。清除它们的过期时间。
const expirationTimes = root.expirationTimes;
let lanes = suspendedLanes;
while (lanes > 0) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
expirationTimes[index] = NoTimestamp;
lanes &= ~lane;
}
if (spawnedLane !== NoLane) {
markSpawnedDeferredLane(root, spawnedLane, suspendedLanes);
}
}
3.标记根已 ping
export function markRootPinged(root: FiberRoot, pingedLanes: Lanes) {
root.pingedLanes |= root.suspendedLanes & pingedLanes;
// The data that just resolved could have unblocked additional children, which
// will also need to be prewarmed if something suspends again.
// 刚刚被解析的数据可能已经解锁了其他子任务,如果再次有任务挂起,这些子任务也需要提前准备。
root.warmLanes &= ~pingedLanes;
}
4.标记根已完成
export function markRootFinished(
root: FiberRoot,
finishedLanes: Lanes,
remainingLanes: Lanes,
spawnedLane: Lane,
updatedLanes: Lanes,
suspendedRetryLanes: Lanes,
) {
const previouslyPendingLanes = root.pendingLanes;
const noLongerPendingLanes = previouslyPendingLanes & ~remainingLanes;
root.pendingLanes = remainingLanes;
// Let's try everything again
// 我们再试一次所有方法
root.suspendedLanes = NoLanes;
root.pingedLanes = NoLanes;
root.warmLanes = NoLanes;
if (enableDefaultTransitionIndicator) {
root.indicatorLanes &= remainingLanes;
}
root.expiredLanes &= remainingLanes;
root.entangledLanes &= remainingLanes;
root.errorRecoveryDisabledLanes &= remainingLanes;
root.shellSuspendCounter = 0;
const entanglements = root.entanglements;
const expirationTimes = root.expirationTimes;
const hiddenUpdates = root.hiddenUpdates;
// Clear the lanes that no longer have pending work
// 清理不再有待处理工作的 Lane
let lanes = noLongerPendingLanes;
while (lanes > 0) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
entanglements[index] = NoLanes;
expirationTimes[index] = NoTimestamp;
const hiddenUpdatesForLane = hiddenUpdates[index];
if (hiddenUpdatesForLane !== null) {
hiddenUpdates[index] = null;
// "Hidden" updates are updates that were made to a hidden component. They
// have special logic associated with them because they may be entangled
// with updates that occur outside that tree. But once the outer tree
// commits, they behave like regular updates.
//
// “隐藏”更新是对隐藏组件所做的更新。
// 它们有特殊的逻辑,因为它们可能与发生在该树之外的更新交织在一起。
// 但一旦外部树提交,它们就像普通更新一样行为。
for (let i = 0; i < hiddenUpdatesForLane.length; i++) {
const update = hiddenUpdatesForLane[i];
if (update !== null) {
update.lane &= ~OffscreenLane;
}
}
}
lanes &= ~lane;
}
if (spawnedLane !== NoLane) {
markSpawnedDeferredLane(
root,
spawnedLane,
// This render finished successfully without suspending, so we don't need
// to entangle the spawned task with the parent task.
// 这个渲染成功完成,没有中断,所以我们不需要把生成的任务与父任务纠缠在一起。
NoLanes,
);
}
// suspendedRetryLanes represents the retry lanes spawned by new Suspense
// boundaries during this render that were not later pinged.
//
// suspendedRetryLanes 表示在此渲染过程中由新的
// Suspense 边界生成但之后未被 ping 的重试 Lane 。
//
// These lanes were marked as pending on their associated Suspense boundary
// fiber during the render phase so that we could start rendering them
// before new data streams in. As soon as the fallback commits, we can try
// to render them again.
//
// 这些 Lane 在渲染阶段被标记为其相关 Suspense 边界的挂起状态,
// 这样我们就可以在新数据到达之前开始渲染它们。
// 一旦回退提交,我们可以尝试再次渲染它们。
//
// But since we know they're still suspended, we can skip straight to the
// "prerender" mode (i.e. don't skip over siblings after something
// suspended) instead of the regular mode (i.e. unwind and skip the siblings
// as soon as something suspends to unblock the rest of the update).
//
// 但由于我们知道它们仍然处于挂起状态,我们可以直接跳到“预渲染”模式
// (即在某些内容挂起后不跳过其兄弟节点),而不是常规模式
// (即在某些内容挂起时立即展开并跳过兄弟节点以解锁其余更新)。
if (
suspendedRetryLanes !== NoLanes &&
// Note that we only do this if there were no updates since we started
// rendering. This mirrors the logic in markRootUpdated — whenever we
// receive an update, we reset all the suspended and pinged lanes.
//
// 注意,我们只有在自开始渲染以来没有更新的情况下才会这样做。
// 这反映了 markRootUpdated 中的逻辑——每当我们收到更新时,
// 都会重置所有挂起和 ping 的 Lane 。
updatedLanes === NoLanes &&
// LegacyRoot 值来自于 ReactRootTags
!(disableLegacyMode && root.tag === LegacyRoot)
) {
// We also need to avoid marking a retry lane as suspended if it was already
// pending before this render. We can't say these are now suspended if they
// weren't included in our attempt.
//
// 我们还需要避免将一个重试队列标记为挂起,如果它在这次渲染之前已经是待处理状态。
// 我们不能说它们现在是挂起的,如果它们不在我们的尝试范围内。
const freshlySpawnedRetryLanes =
suspendedRetryLanes &
// Remove any retry lane that was already pending before our just-finished
// attempt, and also wasn't included in that attempt.
// 移除在我们刚完成的尝试之前已经待处理的任何重试 Lane ,并且也没有包含在该尝试中的重试 Lane 。
~(previouslyPendingLanes & ~finishedLanes);
root.suspendedLanes |= freshlySpawnedRetryLanes;
}
}
5.标记已延迟生成的 Lane
function markSpawnedDeferredLane(
root: FiberRoot,
spawnedLane: Lane,
entangledLanes: Lanes,
) {
// This render spawned a deferred task. Mark it as pending.
// 这个渲染生成了一个延迟任务。将其标记为待处理。
root.pendingLanes |= spawnedLane;
root.suspendedLanes &= ~spawnedLane;
// Entangle the spawned lane with the DeferredLane bit so that we know it
// was the result of another render. This lets us avoid a useDeferredValue
// waterfall — only the first level will defer.
// TODO: Now that there is a reserved set of transition lanes that are used
// exclusively for deferred work, we should get rid of this special
// DeferredLane bit; the same information can be inferred by checking whether
// the lane is one of the TransitionDeferredLanes. The only reason this still
// exists is because we need to also do the same for OffscreenLane. That
// requires additional changes because there are more places around the
// codebase that treat OffscreenLane as a magic value; would need to check
// for a new OffscreenDeferredLane, too. Will leave this for a follow-up.
//
// 将生成的 lane 与 DeferredLane 位纠缠起来,以便我们知道它
// 是另一次渲染的结果。这让我们可以避免 useDeferredValue
// 的层层阻塞 — 只有第一级会被延迟。
// TODO:现在有一组专门用于延迟工作的过渡 lanes,我们应该
// 去掉这个特殊的 DeferredLane 位;相同的信息可以通过检查
// lane 是否属于 TransitionDeferredLanes 来推断。它仍然存在的唯一原因
// 是我们还需要对 OffscreenLane 做同样的处理。这需要额外的更改,
// 因为代码库中还有更多地方将 OffscreenLane 当作魔法值处理;
// 也需要检查新的 OffscreenDeferredLane。这个留到后续处理。
const spawnedLaneIndex = laneToIndex(spawnedLane);
root.entangledLanes |= spawnedLane;
root.entanglements[spawnedLaneIndex] |=
DeferredLane |
// If the parent render task suspended, we must also entangle those lanes
// with the spawned task, so that the deferred task includes all the same
// updates that the parent task did. We can exclude any lane that is not
// used for updates (e.g. Offscreen).
//
// 如果父渲染任务被挂起,我们也必须将那些 Lane 与生成的任务纠缠在一起,
// 这样延迟任务就包含了父任务执行的所有相同更新。
// 我们可以排除任何未用于更新的 Lane (例如 Offscreen)。
(entangledLanes & UpdateLanes);
}
6.标记根部缠绕
export function markRootEntangled(root: FiberRoot, entangledLanes: Lanes) {
// In addition to entangling each of the given lanes with each other, we also
// have to consider _transitive_ entanglements. For each lane that is already
// entangled with *any* of the given lanes, that lane is now transitively
// entangled with *all* the given lanes.
//
// 除了让每个给定的 Lane 彼此纠缠之外,我们还必须考虑_传递_纠缠。对于每个已经与*任意*给定
// Lane 纠缠的 Lane ,该 Lane 现在会通过传递与*所有*给定 Lane 纠缠。
//
// Translated: If C is entangled with A, then entangling A with B also
// entangles C with B.
//
// 翻译:如果 C 与 A 纠缠,那么将 A 与 B 纠缠也会使 C 与 B 纠缠。
//
// If this is hard to grasp, it might help to intentionally break this
// function and look at the tests that fail in ReactTransition-test.js. Try
// commenting out one of the conditions below.
//
// 如果这很难理解,故意破坏这个函数并查看 ReactTransition-test.js 中
// 失败的测试可能会有帮助。尝试注释掉下面的某个条件。
const rootEntangledLanes = (root.entangledLanes |= entangledLanes);
const entanglements = root.entanglements;
let lanes = rootEntangledLanes;
while (lanes) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
if (
// Is this one of the newly entangled lanes?
// 这是新纠缠的 Lane 之一吗?
(lane & entangledLanes) |
// Is this lane transitively entangled with the newly entangled lanes?
// 这个 Lane 是否与新纠缠的 Lane 间接纠缠?
(entanglements[index] & entangledLanes)
) {
entanglements[index] |= entangledLanes;
}
lanes &= ~lane;
}
}
7.将待升级 Lane 同步
export function upgradePendingLanesToSync(
root: FiberRoot,
lanesToUpgrade: Lanes,
) {
// Same as upgradePendingLaneToSync but accepts multiple lanes, so it's a
// bit slower.
// 与 upgradePendingLaneToSync 相同,但接受多个 Lane ,因此稍微慢一些。
root.pendingLanes |= SyncLane;
root.entangledLanes |= SyncLane;
let lanes = lanesToUpgrade;
while (lanes) {
const index = pickArbitraryLaneIndex(lanes);
const lane = 1 << index;
root.entanglements[SyncLaneIndex] |= lane;
lanes &= ~lane;
}
}
8.标记隐藏更新
export function markHiddenUpdate(
root: FiberRoot,
update: ConcurrentUpdate,
lane: Lane,
) {
const index = laneToIndex(lane);
const hiddenUpdates = root.hiddenUpdates;
const hiddenUpdatesForLane = hiddenUpdates[index];
if (hiddenUpdatesForLane === null) {
hiddenUpdates[index] = [update];
} else {
hiddenUpdatesForLane.push(update);
}
update.lane = lane | OffscreenLane;
}
9.获取水合冲突的 Lane
export function getBumpedLaneForHydration(
root: FiberRoot,
renderLanes: Lanes,
): Lane {
const renderLane = getHighestPriorityLane(renderLanes);
const bumpedLane =
(renderLane & SyncUpdateLanes) !== NoLane
? // Unify sync lanes. We don't do this inside getBumpedLaneForHydrationByLane
// because that causes things to flush synchronously when they shouldn't.
// TODO: This is not coherent but that's beacuse the unification is not coherent.
// We need to get merge these into an actual single lane.
//
// 统一同步 Lane 。我们不在 getBumpedLaneForHydrationByLane 内执行此操作
// 因为那会导致在不应该同步刷新时进行同步刷新。
// TODO: 这并不连贯,但这是因为统一本身不连贯。
// 我们需要将它们合并成一个真正的单一 Lane 。
SyncHydrationLane
: getBumpedLaneForHydrationByLane(renderLane);
// Check if the lane we chose is suspended. If so, that indicates that we
// already attempted and failed to hydrate at that level. Also check if we're
// already rendering that lane, which is rare but could happen.
// TODO: This should move into the caller to decide whether giving up is valid.
// 检查我们选择的 Lane 是否被挂起。如果是,这表明我们已经尝试过在该级别进行水合但失败了。
// 还要检查我们是否已经在渲染该 Lane ,这很少见,但可能会发生。
// TODO: 这应该移到调用者处,由调用者决定放弃是否合理。
if ((bumpedLane & (root.suspendedLanes | renderLanes)) !== NoLane) {
// Give up trying to hydrate and fall back to client render.
// 放弃尝试进行水合,转而使用客户端渲染。
return NoLane;
}
return bumpedLane;
}
10.将 Fiber 添加到 Lane 映射
export function addFiberToLanesMap(
root: FiberRoot,
fiber: Fiber,
lanes: Lanes | Lane,
) {
if (!enableUpdaterTracking) {
return;
}
if (!isDevToolsPresent) {
return;
}
const pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
while (lanes > 0) {
const index = laneToIndex(lanes);
const lane = 1 << index;
const updaters = pendingUpdatersLaneMap[index];
updaters.add(fiber);
lanes &= ~lane;
}
}
11.将待处理的 Fiber 移动到已备忘的
export function movePendingFibersToMemoized(root: FiberRoot, lanes: Lanes) {
if (!enableUpdaterTracking) {
return;
}
if (!isDevToolsPresent) {
return;
}
const pendingUpdatersLaneMap = root.pendingUpdatersLaneMap;
const memoizedUpdaters = root.memoizedUpdaters;
while (lanes > 0) {
const index = laneToIndex(lanes);
const lane = 1 << index;
const updaters = pendingUpdatersLaneMap[index];
if (updaters.size > 0) {
updaters.forEach(fiber => {
const alternate = fiber.alternate;
if (alternate === null || !memoizedUpdaters.has(alternate)) {
memoizedUpdaters.add(fiber);
}
});
updaters.clear();
}
lanes &= ~lane;
}
}
12.将过渡添加到 Lane 映射
export function addTransitionToLanesMap(
root: FiberRoot,
transition: Transition,
lane: Lane,
) {
if (enableTransitionTracing) {
const transitionLanesMap = root.transitionLanes;
const index = laneToIndex(lane);
let transitions = transitionLanesMap[index];
if (transitions === null) {
transitions = new Set();
}
transitions.add(transition);
transitionLanesMap[index] = transitions;
}
}
13. 从 Lanes 获取转换
export function getTransitionsForLanes(
root: FiberRoot,
lanes: Lane | Lanes,
): Array<Transition> | null {
if (!enableTransitionTracing) {
return null;
}
const transitionsForLanes = [];
while (lanes > 0) {
const index = laneToIndex(lanes);
const lane = 1 << index;
const transitions = root.transitionLanes[index];
if (transitions !== null) {
transitions.forEach(transition => {
transitionsForLanes.push(transition);
});
}
lanes &= ~lane;
}
if (transitionsForLanes.length === 0) {
return null;
}
return transitionsForLanes;
}
14.清除 Lanes 中的转换
export function clearTransitionsForLanes(root: FiberRoot, lanes: Lane | Lanes) {
if (!enableTransitionTracing) {
return;
}
while (lanes > 0) {
const index = laneToIndex(lanes);
const lane = 1 << index;
const transitions = root.transitionLanes[index];
if (transitions !== null) {
root.transitionLanes[index] = null;
}
lanes &= ~lane;
}
}
廿八、根据冲突 Lane 获取水合 Lane
export function getBumpedLaneForHydrationByLane(lane: Lane): Lane {
switch (lane) {
case SyncLane:
lane = SyncHydrationLane;
break;
case InputContinuousLane:
lane = InputContinuousHydrationLane;
break;
case DefaultLane:
lane = DefaultHydrationLane;
break;
case TransitionLane1:
case TransitionLane2:
case TransitionLane3:
case TransitionLane4:
case TransitionLane5:
case TransitionLane6:
case TransitionLane7:
case TransitionLane8:
case TransitionLane9:
case TransitionLane10:
case TransitionLane11:
case TransitionLane12:
case TransitionLane13:
case TransitionLane14:
case RetryLane1:
case RetryLane2:
case RetryLane3:
case RetryLane4:
lane = TransitionHydrationLane;
break;
case IdleLane:
lane = IdleHydrationLane;
break;
default:
// Everything else is already either a hydration lane, or shouldn't
// be retried at a hydration lane.
// 其他所有内容要么已经是水合 Lane ,要么不应该在水合 Lane 中重试。
lane = NoLane;
break;
}
return lane;
}
廿九、获取最高优先级 Lane 的组名
// Used to name the Performance Track
// 用于命名性能 Line
export function getGroupNameOfHighestPriorityLane(lanes: Lanes): string {
if (
lanes &
(SyncHydrationLane |
SyncLane |
InputContinuousHydrationLane |
InputContinuousLane |
DefaultHydrationLane |
DefaultLane)
) {
return 'Blocking';
}
if (lanes & GestureLane) {
return 'Gesture';
}
if (lanes & (TransitionHydrationLane | TransitionLanes)) {
return 'Transition';
}
if (lanes & RetryLanes) {
return 'Suspense';
}
if (
lanes &
(SelectiveHydrationLane |
IdleHydrationLane |
IdleLane |
OffscreenLane |
DeferredLane)
) {
return 'Idle';
}
return 'Other';
}
三十、变量
1. 下次过渡
let nextTransitionUpdateLane: Lane = TransitionLane1;
let nextTransitionDeferredLane: Lane = TransitionLane11;
let nextRetryLane: Lane = RetryLane1;
卅一、工具
1.获取最高优先级组
function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes {
const pendingSyncLanes = lanes & SyncUpdateLanes;
if (pendingSyncLanes !== 0) {
return pendingSyncLanes;
}
switch (getHighestPriorityLane(lanes)) {
case SyncHydrationLane:
return SyncHydrationLane;
case SyncLane:
return SyncLane;
case InputContinuousHydrationLane:
return InputContinuousHydrationLane;
case InputContinuousLane:
return InputContinuousLane;
case DefaultHydrationLane:
return DefaultHydrationLane;
case DefaultLane:
return DefaultLane;
case GestureLane:
return GestureLane;
case TransitionHydrationLane:
return TransitionHydrationLane;
case TransitionLane1:
case TransitionLane2:
case TransitionLane3:
case TransitionLane4:
case TransitionLane5:
case TransitionLane6:
case TransitionLane7:
case TransitionLane8:
case TransitionLane9:
case TransitionLane10:
return lanes & TransitionUpdateLanes;
case TransitionLane11:
case TransitionLane12:
case TransitionLane13:
case TransitionLane14:
return lanes & TransitionDeferredLanes;
case RetryLane1:
case RetryLane2:
case RetryLane3:
case RetryLane4:
return lanes & RetryLanes;
case SelectiveHydrationLane:
return SelectiveHydrationLane;
case IdleHydrationLane:
return IdleHydrationLane;
case IdleLane:
return IdleLane;
case OffscreenLane:
return OffscreenLane;
case DeferredLane:
// This shouldn't be reachable because deferred work is always entangled
// with something else.
// 这不应该被访问到,因为延迟工作总是与其他事情纠缠在一起。
return NoLanes;
default:
if (__DEV__) {
console.error(
'Should have found matching lanes. This is a bug in React.',
);
}
// This shouldn't be reachable, but as a fallback, return the entire bitmask.
// 这不应该到达这里,但作为备用,返回整个位掩码。
return lanes;
}
}
2. 计算过期时间
function computeExpirationTime(lane: Lane, currentTime: number) {
switch (lane) {
case SyncHydrationLane:
case SyncLane:
case InputContinuousHydrationLane:
case InputContinuousLane:
case GestureLane:
// User interactions should expire slightly more quickly.
// 用户交互的过期时间应该稍微快一些。
//
// NOTE: This is set to the corresponding constant as in Scheduler.js.
// When we made it larger, a product metric in www regressed, suggesting
// there's a user interaction that's being starved by a series of
// synchronous updates. If that theory is correct, the proper solution is
// to fix the starvation. However, this scenario supports the idea that
// expiration times are an important safeguard when starvation
// does happen.
//
// 注意:这个值设置为与 Scheduler.js 中相应常量相同。
// 当我们将其设置得更大时,www 中的一个产品指标下降,这表明
// 有一个用户交互被一系列同步更新所阻塞。如果这个理论是正确的,
// 正确的解决方案是修复阻塞问题。然而,这种情况支持了这样一个观点:
// 当阻塞发生时,过期时间是一个重要的保护措施。
return currentTime + syncLaneExpirationMs;
case DefaultHydrationLane:
case DefaultLane:
case TransitionHydrationLane:
case TransitionLane1:
case TransitionLane2:
case TransitionLane3:
case TransitionLane4:
case TransitionLane5:
case TransitionLane6:
case TransitionLane7:
case TransitionLane8:
case TransitionLane9:
case TransitionLane10:
case TransitionLane11:
case TransitionLane12:
case TransitionLane13:
case TransitionLane14:
return currentTime + transitionLaneExpirationMs;
case RetryLane1:
case RetryLane2:
case RetryLane3:
case RetryLane4:
// TODO: Retries should be allowed to expire if they are CPU bound for
// too long, but when I made this change it caused a spike in browser
// crashes. There must be some other underlying bug; not super urgent but
// ideally should figure out why and fix it. Unfortunately we don't have
// a repro for the crashes, only detected via production metrics.
//
// TODO: 如果重试过长时间受 CPU 限制,应该允许它们过期,但当我做这个更改时,
// 导致浏览器崩溃激增。一定还有其他潜在的 bug;不是特别紧急,但最好找出原因并修复。不
// 幸的是,我们没有崩溃的复现方法,只能通过生产指标检测到。
return enableRetryLaneExpiration
? currentTime + retryLaneExpirationMs
: NoTimestamp;
case SelectiveHydrationLane:
case IdleHydrationLane:
case IdleLane:
case OffscreenLane:
case DeferredLane:
// Anything idle priority or lower should never expire.
// 任何空闲优先级或更低的都不应过期。
return NoTimestamp;
default:
if (__DEV__) {
console.error(
'Should have found matching lanes. This is a bug in React.',
);
}
return NoTimestamp;
}
}
3.选择任意 Lane 索引
备注
clz32 来自于文件 'clz32'
function pickArbitraryLaneIndex(lanes: Lanes) {
return 31 - clz32(lanes);
}
4. Lane 转换为索引
function laneToIndex(lane: Lane) {
return pickArbitraryLaneIndex(lane);
}