// Track lanes that were updated during the render phase workInProgressRootRenderPhaseUpdatedLanes = mergeLanes( workInProgressRootRenderPhaseUpdatedLanes, lane, ); } else {
// ...
// 页面初次渲染时候,此时workInProgressRoot还是null if (root === workInProgressRoot) { // Received an update to a tree that's in the middle of rendering. Mark // that there was an interleaved update work on this root. if ((executionContext & RenderContext) === NoContext) { workInProgressRootInterleavedUpdatedLanes = mergeLanes( workInProgressRootInterleavedUpdatedLanes, lane, ); } if (workInProgressRootExitStatus === RootSuspendedWithDelay) { // The root already suspended with a delay, which means this render // definitely won't finish. Since we have a new update, let's mark it as // suspended now, right before marking the incoming update. This has the // effect of interrupting the current render and switching to the update. // TODO: Make sure this doesn't override pings that happen while we've // already started rendering. markRootSuspended( root, workInProgressRootRenderLanes, workInProgressDeferredLane, ); } }
// 2. 开始调度 ensureRootIsScheduled(root);
if ( lane === SyncLane && executionContext === NoContext && (fiber.mode & ConcurrentMode) === NoMode ) { if (__DEV__ && ReactCurrentActQueue.isBatchingLegacy) { // Treat `act` as if it's inside `batchedUpdates`, even in legacy mode. } else { // Flush the synchronous work now, unless we're already working or inside // a batch. This is intentionally inside scheduleUpdateOnFiber instead of // scheduleCallbackForFiber to preserve the ability to schedule a callback // without immediately flushing it. We only do this for user-initiated // updates, to preserve historical behavior of legacy mode. resetRenderTimer(); flushSyncWorkOnLegacyRootsOnly(); } } } }
// 在当前事件结束时,逐个检查每个root,并确保为每个root安排了正确优先级的任务。 if (__DEV__ && ReactCurrentActQueue.current !== null) { // ... } else { if (!didScheduleMicrotask) { // ? sy didScheduleMicrotask = true; scheduleImmediateTask(processRootScheduleInMicrotask); } }
if (!enableDeferRootSchedulingToMicrotask) { // While this flag is disabled, we schedule the render task immediately // instead of waiting a microtask. // TODO: We need to land enableDeferRootSchedulingToMicrotask ASAP to // unblock additional features we have planned. scheduleTaskForRootDuringMicrotask(root, now()); } }
functionscheduleImmediateTask(cb: () => mixed) { // TODO: Can we land supportsMicrotasks? Which environments don't support it? // Alternatively, can we move this check to the host config? if (supportsMicrotasks) { // sy- scheduleMicrotask(() => { // In Safari, appending an iframe forces microtasks to run. // 在Safari中,附加一个iframe会强制微任务运行。 // https://github.com/facebook/react/issues/22459 // We don't support running callbacks in the middle of render // or commit so we need to check against that. // 在渲染或提交过程中我们不支持运行回调函数,因此我们需要进行检查。 const executionContext = getExecutionContext(); if ((executionContext & (RenderContext | CommitContext)) !== NoContext) { // sy-no // Note that this would still prematurely flush the callbacks // if this happens outside render or commit phase (e.g. in an event). // 如果这种情况发生在render或commit阶段之外(例如在事件中),这仍然会过早地刷新回调函数。
// Intentionally using a macrotask instead of a microtask here. This is // wrong semantically but it prevents an infinite loop. The bug is // Safari's, not ours, so we just do our best to not crash even though // the behavior isn't completely correct. // 在这里故意使用宏任务而不是微任务。这在语义上是错误的,但它可以防止无限循环。 // 这个 bug 是 Safari 的问题,而不是我们的问题,所以我们尽力避免崩溃,即使行为并不完全正确。 Scheduler_scheduleCallback(ImmediateSchedulerPriority, cb); return; } cb(); }); } else { // If microtasks are not supported, use Scheduler. Scheduler_scheduleCallback(ImmediateSchedulerPriority, cb); } }
let prev = null; let root = firstScheduledRoot; while (root !== null) {
const next = root.next;
if ( currentEventTransitionLane !== NoLane && shouldAttemptEagerTransition() ) { // A transition was scheduled during an event, but we're going to try to // render it synchronously anyway. We do this during a popstate event to // preserve the scroll position of the previous page. upgradePendingLaneToSync(root, currentEventTransitionLane); }
const nextLanes = scheduleTaskForRootDuringMicrotask(root, currentTime); if (nextLanes === NoLane) { // ? sy second // 页面初次渲染,再执行这里。nextLanes=0 // This root has no more pending work. Remove it from the schedule. To // guard against subtle reentrancy bugs, this microtask is the only place // we do this — you can add roots to the schedule whenever, but you can // only remove them here.
// Null this out so we know it's been removed from the schedule. //root 没有更多pending work。从调度中移除它。为了防止微妙的重入bug,这个microtask是我们唯一执行此操作的地方 — 你可以随时将roots添加到调度中,但只能在这里将它们移除。 // 将其置null,以便我们知道它已从调度中移除 root.next = null; if (prev === null) { // ? sy // This is the new head of the list firstScheduledRoot = next; } else { prev.next = next; } if (next === null) { // ? sy // This is the new tail of the list lastScheduledRoot = prev; } } else { // ? sy first // 页面初次渲染,先执行这里。nextLanes=32 // This root still has work. Keep it in the list. prev = root; if (includesSyncLane(nextLanes)) { // ? sy-no mightHavePendingSyncWork = true; } } root = next; }
currentEventTransitionLane = NoLane;
// At the end of the microtask, flush any pending synchronous work. This has // to come at the end, because it does actual rendering work that might throw. // 在microtask结束时,flush任何pending的同步work。这必须放在最后,因为它执行实际的可能会抛出异常的渲染工作。 // onClick count flushSyncWorkOnAllRoots(); }
// Determine the next lanes to work on, and their priority. const workInProgressRoot = getWorkInProgressRoot(); const workInProgressRootRenderLanes = getWorkInProgressRootRenderLanes();
const existingCallbackNode = root.callbackNode; // 如果优先级等于0 ,说明根节点没有可处理的回调,则退出任务调度 if ( // Check if there's nothing to work on nextLanes === NoLanes || // If this root is currently suspended and waiting for data to resolve, don't // schedule a task to render it. We'll either wait for a ping, or wait to // receive an update. // // Suspended render phase (root === workInProgressRoot && isWorkLoopSuspendedOnData()) || // Suspended commit phase root.cancelPendingCommit !== null ) { // Fast path: There's nothing to work on. if (existingCallbackNode !== null) { cancelCallback(existingCallbackNode); } root.callbackNode = null; root.callbackPriority = NoLane; return NoLane; }
// Schedule a new callback in the host environment. if (includesSyncLane(nextLanes)) { // sy- setState // sy-no 初次渲染 // Synchronous work is always flushed at the end of the microtask, so we // don't need to schedule an additional task. // 同步工作始终在微任务结束时刷新,因此我们不需要安排额外的任务。 if (existingCallbackNode !== null) { cancelCallback(existingCallbackNode); } root.callbackPriority = SyncLane; root.callbackNode = null; return SyncLane; } else { // We use the highest priority lane to represent the priority of the callback. const existingCallbackPriority = root.callbackPriority; const newCallbackPriority = getHighestPriorityLane(nextLanes);
if ( newCallbackPriority === existingCallbackPriority && // Special case related to `act`. If the currently scheduled task is a // Scheduler task, rather than an `act` task, cancel it and re-schedule // on the `act` queue. !( __DEV__ && ReactCurrentActQueue.current !== null && existingCallbackNode !== fakeActCallbackNode ) ) { // The priority hasn't changed. We can reuse the existing task. return newCallbackPriority; } else { // Cancel the existing callback. We'll schedule a new one below. cancelCallback(existingCallbackNode); }
// 定义一个【调度的优先级】,因为react事件优先级和scheduler的优先级不同,需要经过转换【即:事件优先级转换为调度优先级】 let schedulerPriorityLevel; // lanes转换成事件优先级,匹配符合的优先级,然后赋值对应的scheduler的优先级 switch (lanesToEventPriority(nextLanes)) { case DiscreteEventPriority: schedulerPriorityLevel = ImmediateSchedulerPriority; break; case ContinuousEventPriority: schedulerPriorityLevel = UserBlockingSchedulerPriority; break; case DefaultEventPriority: // 32 // ? sy 页面初次渲染、transition(128) schedulerPriorityLevel = NormalSchedulerPriority; // 3 break; case IdleEventPriority: schedulerPriorityLevel = IdleSchedulerPriority; break; default: schedulerPriorityLevel = NormalSchedulerPriority; break; }