// Timeout handle returned by setTimeout. Used to cancel a pending timeout, if // it's superseded by a new one. timeoutHandle: TimeoutHandle | NoTimeout, // Top context object, used by renderSubtreeIntoContainer context: Object | null, pendingContext: Object | null, // Determines if we should attempt to hydrate on the initial mount +hydrate: boolean,
// List of top-level batches. This list indicates whether a commit should be // deferred. Also contains completion callbacks. // TODO: Lift this into the renderer firstBatch: Batch | null,
if ( // If we're in the render phase, we don't need to schedule this root // for an update, because we'll do it before we exit... !isWorking || isCommitting || // ...unless this is a different root than the one we're rendering. nextRoot !== root ) { const rootExpirationTime = root.expirationTime; // 开始进入请求工作 // react应用产生的所有更新, 都交由FiberRoot来处理. requestWork(root, rootExpirationTime); } }
functionperformAsyncWork(didTimeout) { // 如果FiberRoot的expirationTime超时 if (didTimeout) { if (firstScheduledRoot !== null) { // 更新FiberRoot单向循环链表, 更新所有已经超时的expirationTime // 为了安排它们在一个批次进行批量更新 recomputeCurrentRendererTime(); let root: FiberRoot = firstScheduledRoot; do { didExpireAtExpirationTime(root, currentRendererTime); // The root schedule is circular, so this is never null. root = (root.nextScheduledRoot: any); } while (root !== firstScheduledRoot); } }
findHighestPriorityRoot();
if (disableYielding) { while (nextFlushedRoot !== null && nextFlushedExpirationTime !== NoWork) { performWorkOnRoot(nextFlushedRoot, nextFlushedExpirationTime, false); findHighestPriorityRoot(); } }
// If we're inside a callback, set this to false since we just completed it. callbackExpirationTime = NoWork; callbackID = null;
let finishedWork = root.finishedWork; if (finishedWork !== null) { completeRoot(root, finishedWork, expirationTime); } else { renderRoot(root, isYieldy); finishedWork = root.finishedWork; if (finishedWork !== null) { // We've completed the root. Commit it. completeRoot(root, finishedWork, expirationTime); } } } else { // ! 异步任务
let finishedWork = root.finishedWork; if (finishedWork !== null) { // This root is already complete. We can commit it. completeRoot(root, finishedWork, expirationTime); } else { renderRoot(root, isYieldy); completeRoot(root, finishedWork, expirationTime); } }
do { try { // 大循环, 遍历整颗WorkInProgress树 workLoop(isYieldy); } catch (thrownValue) { // 如果出现错误, 对应的错误处理机制 if (nextUnitOfWork === null) { // This is a fatal error. didFatal = true; onUncaughtError(thrownValue); } else { const sourceFiber: Fiber = nextUnitOfWork; let returnFiber = sourceFiber.return; if (returnFiber === null) { // This is the root. The root could capture its own errors. However, // we don't know if it errors before or after we pushed the host // context. This information is needed to avoid a stack mismatch. // Because we're not sure, treat this as a fatal error. We could track // which phase it fails in, but doesn't seem worth it. At least // for now. didFatal = true; onUncaughtError(thrownValue); } else { throwException( root, returnFiber, sourceFiber, thrownValue, nextRenderExpirationTime, ); nextUnitOfWork = completeUnitOfWork(sourceFiber); continue; } } } break; } while (true);
// Yield back to main thread. // 如果渲染出现致命错误, 会退出并重新安排渲染 if (didFatal) { onFatal(root); return; }
// We completed the whole tree. const rootWorkInProgress = root.current.alternate;
// Ready to commit. // 进入commit阶段 onComplete(root, rootWorkInProgress, expirationTime); }
if (oldProps !== newProps || hasLegacyContextChanged()) { // If props or context changed, mark the fiber as having performed work. // This may be unset if the props are determined to be equal later (memo).
didReceiveUpdate = false; // This fiber does not have any pending work. Bailout without entering // the begin phase. There's still some bookkeeping we that needs to be done // in this optimized path, mostly pushing stuff onto the stack. switch (workInProgress.tag) { case HostRoot: pushHostRootContext(workInProgress); resetHydrationState(); break;
// ! HostComponent -> 代表原生DOM元素的字符串(div、p) case HostComponent: pushHostContext(workInProgress); break; case ClassComponent: { const Component = workInProgress.type; if (isLegacyContextProvider(Component)) { pushLegacyContextProvider(workInProgress); } break; } case HostPortal: pushHostContainer( workInProgress, workInProgress.stateNode.containerInfo, ); break; case ContextProvider: { const newValue = workInProgress.memoizedProps.value; pushProvider(workInProgress, newValue); break; } case Profiler: if (enableProfilerTimer) { workInProgress.effectTag |= Update; } break; case SuspenseComponent: { const state: SuspenseState | null = workInProgress.memoizedState; const didTimeout = state !== null; if (didTimeout) { // If this boundary is currently timed out, we need to decide // whether to retry the primary children, or to skip over it and // go straight to the fallback. Check the priority of the primary // child fragment. const primaryChildFragment: Fiber = (workInProgress.child: any); const primaryChildExpirationTime = primaryChildFragment.childExpirationTime; if ( primaryChildExpirationTime !== NoWork && primaryChildExpirationTime >= renderExpirationTime ) { // The primary children have pending work. Use the normal path // to attempt to render the primary children again. return updateSuspenseComponent( current, workInProgress, renderExpirationTime, ); } else { // The primary children do not have pending work with sufficient // priority. Bailout. const child = bailoutOnAlreadyFinishedWork( current, workInProgress, renderExpirationTime, ); if (child !== null) { // The fallback children have pending work. Skip over the // primary children and work on the fallback. return child.sibling; } else { returnnull; } } } break; } case DehydratedSuspenseComponent: { if (enableSuspenseServerRenderer) { // We know that this component will suspend again because if it has // been unsuspended it has committed as a regular Suspense component. // If it needs to be retried, it should have work scheduled on it. workInProgress.effectTag |= DidCapture; break; } } } return bailoutOnAlreadyFinishedWork( current, workInProgress, renderExpirationTime, ); } } else { didReceiveUpdate = false; }
// Before entering the begin phase, clear the expiration time. workInProgress.expirationTime = NoWork;