// If the Fiber is currently active in the "render" phase, // This marks the time at which the work began. // This field is only set when the enableProfilerTimer flag is enabled. actualStartTime?: number,
// Duration of the most recent render time for this Fiber. // This value is not updated when we bailout for memoization purposes. // This field is only set when the enableProfilerTimer flag is enabled. selfBaseDuration?: number,
// Sum of base times for all descedents of this Fiber. // This value bubbles up during the "complete" phase. // This field is only set when the enableProfilerTimer flag is enabled. treeBaseDuration?: number,
// Conceptual aliases // workInProgress : Fiber -> alternate The alternate used for reuse happens // to be the same as work in progress. // __DEV__ only _debugID?: number, _debugSource?: Source | null, _debugOwner?: Fiber | null, _debugIsCurrentlyTiming?: boolean, |};
if ( !isWorking && nextRenderExpirationTime !== NoWork && expirationTime < nextRenderExpirationTime ) { // This is an interruption. (Used for performance tracking.) interruptedBy = fiber; resetStack(); } markPendingPriorityLevel(root, expirationTime); 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; requestWork(root, rootExpirationTime); } if (nestedUpdateCount > NESTED_UPDATE_LIMIT) { // 防止无限循环地嵌套更新 // Reset this back to zero so subsequent updates don't throw. nestedUpdateCount = 0; invariant( false, 'Maximum update depth exceeded. This can happen when a ' + 'component repeatedly calls setState inside ' + 'componentWillUpdate or componentDidUpdate. React limits ' + 'the number of nested updates to prevent infinite loops.', ); } }
functionrequestWork(root: FiberRoot, expirationTime: ExpirationTime) { // 将这个root加入到调度队列,这里主要考虑了有多个的Fiber树的情况。 addRootToSchedule(root, expirationTime); if (isRendering) { // 如果处于 render 阶段,函数执行结束,不需要再次调度。 // Prevent reentrancy. Remaining work will be scheduled at the end of // the currently rendering batch. return; }
if (isBatchingUpdates) { // Flush work at the end of the batch. if (isUnbatchingUpdates) { // 这里如何是强制不批量更新,我们直接调度。 // ...unless we're inside unbatchedUpdates, in which case we should // flush it now. nextFlushedRoot = root; nextFlushedExpirationTime = Sync; performWorkOnRoot(root, Sync, true); } return; }
// TODO: Get rid of Sync and use current time? if (expirationTime === Sync) { performSyncWork(); } else { scheduleCallbackWithExpirationTime(root, expirationTime); } }
//将这个root加入到root调度队列。 functionaddRootToSchedule(root: FiberRoot, expirationTime: ExpirationTime) { // Add the root to the schedule. // Check if this root is already part of the schedule. if (root.nextScheduledRoot === null) { // This root is not already scheduled. Add it. root.expirationTime = expirationTime; if (lastScheduledRoot === null) { firstScheduledRoot = lastScheduledRoot = root; root.nextScheduledRoot = root; } else { lastScheduledRoot.nextScheduledRoot = root; lastScheduledRoot = root; lastScheduledRoot.nextScheduledRoot = firstScheduledRoot; } } else { // This root is already scheduled, but its priority may have increased. const remainingExpirationTime = root.expirationTime; if ( remainingExpirationTime === NoWork || expirationTime < remainingExpirationTime ) { // Update the priority. root.expirationTime = expirationTime; } } }
// We're done flushing work. Either we ran out of time in this callback, // or there's no more work left with sufficient priority.
// If we're inside a callback, set this to false since we just completed it. if (deadline !== null) { callbackExpirationTime = NoWork; callbackID = null; } // If there's work left over, schedule a new callback. if (nextFlushedExpirationTime !== NoWork) { scheduleCallbackWithExpirationTime( ((nextFlushedRoot: any): FiberRoot), nextFlushedExpirationTime, ); }
functionperformWorkOnRoot( root: FiberRoot, expirationTime: ExpirationTime, isExpired: boolean, ) { invariant( !isRendering, 'performWorkOnRoot was called recursively. This error is likely caused ' + 'by a bug in React. Please file an issue.', );
isRendering = true;
// Check if this is async work or sync/expired work. if (deadline === null || isExpired) { // Flush work without yielding. // TODO: Non-yieldy work does not necessarily imply expired work. A renderer // may want to perform some work without yielding, but also without // requiring the root to complete (by triggering placeholders).
let finishedWork = root.finishedWork; if (finishedWork !== null) { // This root is already complete. We can commit it. completeRoot(root, finishedWork, expirationTime); } else { root.finishedWork = null; // If this root previously suspended, clear its existing timeout, since // we're about to try rendering again. const timeoutHandle = root.timeoutHandle; if (timeoutHandle !== noTimeout) { root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above cancelTimeout(timeoutHandle); } // isYieldy判断异步任务是否可以中断. const isYieldy = false; renderRoot(root, isYieldy, isExpired); finishedWork = root.finishedWork; if (finishedWork !== null) { // We've completed the root. Commit it. completeRoot(root, finishedWork, expirationTime); } } } else { // Flush async work. let finishedWork = root.finishedWork; if (finishedWork !== null) { // This root is already complete. We can commit it. completeRoot(root, finishedWork, expirationTime); } else { root.finishedWork = null; // If this root previously suspended, clear its existing timeout, since // we're about to try rendering again. const timeoutHandle = root.timeoutHandle; if (timeoutHandle !== noTimeout) { root.timeoutHandle = noTimeout; // $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check above cancelTimeout(timeoutHandle); } const isYieldy = true; renderRoot(root, isYieldy, isExpired); finishedWork = root.finishedWork; if (finishedWork !== null) { // We've completed the root. Check the deadline one more time // before committing. if (!shouldYield()) { // Still time left. Commit the root. completeRoot(root, finishedWork, expirationTime); } else { // There's no time left. Mark this root as complete. We'll come // back and commit it later. root.finishedWork = finishedWork; } } } }
functionperformUnitOfWork(workInProgress) { let next = beginWork(workInProgress); if (next === null) { next = completeUnitOfWork(workInProgress); } return next; }
functionbeginWork(workInProgress) { console.log('work performed for ' + workInProgress.name); return workInProgress.child; }
functioncompleteUnitOfWork(workInProgress) { while (true) { let returnFiber = workInProgress.return; let siblingFiber = workInProgress.sibling;
nextUnitOfWork = completeWork(workInProgress);
if (siblingFiber !== null) { // If there is a sibling, return it // to perform work for this sibling return siblingFiber; } elseif (returnFiber !== null) { // If there's no more work in this returnFiber, // continue the loop to complete the parent. workInProgress = returnFiber; continue; } else { // We've reached the root. returnnull; } } }
functioncompleteWork(workInProgress) { console.log('work completed for ' + workInProgress.name); returnnull; }