render阶段流程图

一、renderRootSync函数
function renderRootSync(root, lanes) {var prevExecutionContext = executionContext;executionContext |= RenderContext;var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack// and prepare a fresh one. Otherwise we'll continue where we left off.if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {{if (isDevToolsPresent) {var memoizedUpdaters = root.memoizedUpdaters;if (memoizedUpdaters.size > 0) {restorePendingUpdaters(root, workInProgressRootRenderLanes);memoizedUpdaters.clear();} // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.// If we bailout on this work, we'll move them back (like above).// It's important to move them now in case the work spawns more work at the same priority with different updaters.// That way we can keep the current update and future updates separate.movePendingFibersToMemoized(root, lanes);}}workInProgressTransitions = getTransitionsForLanes();prepareFreshStack(root, lanes);}{markRenderStarted(lanes);}do {try {workLoopSync();break;} catch (thrownValue) {handleError(root, thrownValue);}} while (true);resetContextDependencies();executionContext = prevExecutionContext;popDispatcher(prevDispatcher);if (workInProgress !== null) {// This is a sync render, so we should have finished the whole tree.throw new Error('Cannot commit an incomplete root. This error is likely caused by a ' + 'bug in React. Please file an issue.');}{markRenderStopped();} // Set this to null to indicate there's no in-progress render.workInProgressRoot = null;workInProgressRootRenderLanes = NoLanes;return workInProgressRootExitStatus;
} // The work loop is an extremely hot path. Tell Closure not to inline it./** @noinline */function workLoopSync() {// Already timed out, so perform work without checking if we need to yield.while (workInProgress !== null) {performUnitOfWork(workInProgress);}
}
二、renderRootConcurrent函数
function renderRootConcurrent(root, lanes) { var prevExecutionContext = executionContext;executionContext |= RenderContext;var prevDispatcher = pushDispatcher(); // If the root or lanes have changed, throw out the existing stack// and prepare a fresh one. Otherwise we'll continue where we left off.if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {{if (isDevToolsPresent) {var memoizedUpdaters = root.memoizedUpdaters;if (memoizedUpdaters.size > 0) {restorePendingUpdaters(root, workInProgressRootRenderLanes);memoizedUpdaters.clear();} // At this point, move Fibers that scheduled the upcoming work from the Map to the Set.// If we bailout on this work, we'll move them back (like above).// It's important to move them now in case the work spawns more work at the same priority with different updaters.// That way we can keep the current update and future updates separate.movePendingFibersToMemoized(root, lanes);}}workInProgressTransitions = getTransitionsForLanes();resetRenderTimer();prepareFreshStack(root, lanes);}{markRenderStarted(lanes);}do {try {workLoopConcurrent();break;} catch (thrownValue) {handleError(root, thrownValue);}} while (true);resetContextDependencies();popDispatcher(prevDispatcher);executionContext = prevExecutionContext;if (workInProgress !== null) {// Still work remaining.{markRenderYielded();}return RootInProgress;} else {// Completed the tree.{markRenderStopped();} // Set this to null to indicate there's no in-progress render.workInProgressRoot = null;workInProgressRootRenderLanes = NoLanes; // Return the final exit status.return workInProgressRootExitStatus;}
}
三、workLoopSync函数
function workLoopSync() {// Already timed out, so perform work without checking if we need to yield.while (workInProgress !== null) {performUnitOfWork(workInProgress);}
}
四、workLoopConcurrent函数
function workLoopConcurrent() {// Perform work until Scheduler asks us to yieldwhile (workInProgress !== null && !shouldYield()) {performUnitOfWork(workInProgress);}
}
五、performUnitOfWork函数
function performUnitOfWork(unitOfWork) {// The current, flushed, state of this fiber is the alternate. Ideally// nothing should rely on this, but relying on it here means that we don't// need an additional field on the work in progress.var current = unitOfWork.alternate;setCurrentFiber(unitOfWork);var next;if ( (unitOfWork.mode & ProfileMode) !== NoMode) {startProfilerTimer(unitOfWork);next = beginWork$1(current, unitOfWork, subtreeRenderLanes);stopProfilerTimerIfRunningAndRecordDelta(unitOfWork, true);} else {next = beginWork$1(current, unitOfWork, subtreeRenderLanes);}resetCurrentFiber();unitOfWork.memoizedProps = unitOfWork.pendingProps;if (next === null) {// If this doesn't spawn new work, complete the current work.completeUnitOfWork(unitOfWork);} else {workInProgress = next;}ReactCurrentOwner$2.current = null;
}
六、beginWork函数
function beginWork(current, workInProgress, renderLanes) {{if (workInProgress._debugNeedsRemount && current !== null) {// This will restart the begin phase with a new fiber.return remountFiber(current, workInProgress, createFiberFromTypeAndProps(workInProgress.type, workInProgress.key, workInProgress.pendingProps, workInProgress._debugOwner || null, workInProgress.mode, workInProgress.lanes));}}if (current !== null) {var oldProps = current.memoizedProps;var newProps = workInProgress.pendingProps;if (oldProps !== newProps || hasContextChanged() || ( // Force a re-render if the implementation changed due to hot reload:workInProgress.type !== current.type )) {// 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 = true;} else {// Neither props nor legacy context changes. Check if there's a pending// update or context change.var hasScheduledUpdateOrContext = checkScheduledUpdateOrContext(current, renderLanes);if (!hasScheduledUpdateOrContext && // If this is the second pass of an error or suspense boundary, there// may not be work scheduled on `current`, so we check for this flag.(workInProgress.flags & DidCapture) === NoFlags) {// No pending updates or context. Bail out now.didReceiveUpdate = false;return attemptEarlyBailoutIfNoScheduledUpdate(current, workInProgress, renderLanes);}if ((current.flags & ForceUpdateForLegacySuspense) !== NoFlags) {// This is a special case that only exists for legacy mode.// See https://github.com/facebook/react/pull/19216.didReceiveUpdate = true;} else {// An update was scheduled on this fiber, but there are no new props// nor legacy context. Set this to false. If an update queue or context// consumer produces a changed value, it will set this to true. Otherwise,// the component will assume the children have not changed and bail out.didReceiveUpdate = false;}}} else {didReceiveUpdate = false;if (getIsHydrating() && isForkedChild(workInProgress)) {// Check if this child belongs to a list of muliple children in// its parent.//// In a true multi-threaded implementation, we would render children on// parallel threads. This would represent the beginning of a new render// thread for this subtree.//// We only use this for id generation during hydration, which is why the// logic is located in this special branch.var slotIndex = workInProgress.index;var numberOfForks = getForksAtLevel();pushTreeId(workInProgress, numberOfForks, slotIndex);}} // Before entering the begin phase, clear pending update priority.// TODO: This assumes that we're about to evaluate the component and process// the update queue. However, there's an exception: SimpleMemoComponent// sometimes bails out later in the begin phase. This indicates that we should// move this assignment out of the common path and into each branch.workInProgress.lanes = NoLanes;switch (workInProgress.tag) {case IndeterminateComponent:{return mountIndeterminateComponent(current, workInProgress, workInProgress.type, renderLanes);}case LazyComponent:{var elementType = workInProgress.elementType;return mountLazyComponent(current, workInProgress, elementType, renderLanes);}case FunctionComponent:{var Component = workInProgress.type;var unresolvedProps = workInProgress.pendingProps;var resolvedProps = workInProgress.elementType === Component ? unresolvedProps : resolveDefaultProps(Component, unresolvedProps);return updateFunctionComponent(current, workInProgress, Component, resolvedProps, renderLanes);}case ClassComponent:{var _Component = workInProgress.type;var _unresolvedProps = workInProgress.pendingProps;var _resolvedProps = workInProgress.elementType === _Component ? _unresolvedProps : resolveDefaultProps(_Component, _unresolvedProps);return updateClassComponent(current, workInProgress, _Component, _resolvedProps, renderLanes);}case HostRoot:return updateHostRoot(current, workInProgress, renderLanes);case HostComponent:return updateHostComponent(current, workInProgress, renderLanes);case HostText:return updateHostText(current, workInProgress);case SuspenseComponent:return updateSuspenseComponent(current, workInProgress, renderLanes);case HostPortal:return updatePortalComponent(current, workInProgress, renderLanes);case ForwardRef:{var type = workInProgress.type;var _unresolvedProps2 = workInProgress.pendingProps;var _resolvedProps2 = workInProgress.elementType === type ? _unresolvedProps2 : resolveDefaultProps(type, _unresolvedProps2);return updateForwardRef(current, workInProgress, type, _resolvedProps2, renderLanes);}case Fragment:return updateFragment(current, workInProgress, renderLanes);case Mode:return updateMode(current, workInProgress, renderLanes);case Profiler:return updateProfiler(current, workInProgress, renderLanes);case ContextProvider:return updateContextProvider(current, workInProgress, renderLanes);case ContextConsumer:return updateContextConsumer(current, workInProgress, renderLanes);case MemoComponent:{var _type2 = workInProgress.type;var _unresolvedProps3 = workInProgress.pendingProps; // Resolve outer props first, then resolve inner props.var _resolvedProps3 = resolveDefaultProps(_type2, _unresolvedProps3);{if (workInProgress.type !== workInProgress.elementType) {var outerPropTypes = _type2.propTypes;if (outerPropTypes) {checkPropTypes(outerPropTypes, _resolvedProps3, // Resolved for outer only'prop', getComponentNameFromType(_type2));}}}_resolvedProps3 = resolveDefaultProps(_type2.type, _resolvedProps3);return updateMemoComponent(current, workInProgress, _type2, _resolvedProps3, renderLanes);}case SimpleMemoComponent:{return updateSimpleMemoComponent(current, workInProgress, workInProgress.type, workInProgress.pendingProps, renderLanes);}case IncompleteClassComponent:{var _Component2 = workInProgress.type;var _unresolvedProps4 = workInProgress.pendingProps;var _resolvedProps4 = workInProgress.elementType === _Component2 ? _unresolvedProps4 : resolveDefaultProps(_Component2, _unresolvedProps4);return mountIncompleteClassComponent(current, workInProgress, _Component2, _resolvedProps4, renderLanes);}case SuspenseListComponent:{return updateSuspenseListComponent(current, workInProgress, renderLanes);}case ScopeComponent:{break;}case OffscreenComponent:{return updateOffscreenComponent(current, workInProgress, renderLanes);}}throw new Error("Unknown unit of work tag (" + workInProgress.tag + "). This error is likely caused by a bug in " + 'React. Please file an issue.');
}
七、completeUnitOfwork函数
function completeUnitOfWork(unitOfWork) {// Attempt to complete the current unit of work, then move to the next// sibling. If there are no more siblings, return to the parent fiber.var completedWork = unitOfWork;do {// The current, flushed, state of this fiber is the alternate. Ideally// nothing should rely on this, but relying on it here means that we don't// need an additional field on the work in progress.var current = completedWork.alternate;var returnFiber = completedWork.return; // Check if the work completed or if something threw.if ((completedWork.flags & Incomplete) === NoFlags) {setCurrentFiber(completedWork);var next = void 0;if ( (completedWork.mode & ProfileMode) === NoMode) {next = completeWork(current, completedWork, subtreeRenderLanes);} else {startProfilerTimer(completedWork);next = completeWork(current, completedWork, subtreeRenderLanes); // Update render duration assuming we didn't error.stopProfilerTimerIfRunningAndRecordDelta(completedWork, false);}resetCurrentFiber();if (next !== null) {// Completing this fiber spawned new work. Work on that next.workInProgress = next;return;}} else {// This fiber did not complete because something threw. Pop values off// the stack without entering the complete phase. If this is a boundary,// capture values if possible.var _next = unwindWork(current, completedWork); // Because this fiber did not complete, don't reset its lanes.if (_next !== null) {// If completing this work spawned new work, do that next. We'll come// back here again.// Since we're restarting, remove anything that is not a host effect// from the effect tag._next.flags &= HostEffectMask;workInProgress = _next;return;}if ( (completedWork.mode & ProfileMode) !== NoMode) {// Record the render duration for the fiber that errored.stopProfilerTimerIfRunningAndRecordDelta(completedWork, false); // Include the time spent working on failed children before continuing.var actualDuration = completedWork.actualDuration;var child = completedWork.child;while (child !== null) {actualDuration += child.actualDuration;child = child.sibling;}completedWork.actualDuration = actualDuration;}if (returnFiber !== null) {// Mark the parent fiber as incomplete and clear its subtree flags.returnFiber.flags |= Incomplete;returnFiber.subtreeFlags = NoFlags;returnFiber.deletions = null;} else {// We've unwound all the way to the root.workInProgressRootExitStatus = RootDidNotComplete;workInProgress = null;return;}}var siblingFiber = completedWork.sibling;if (siblingFiber !== null) {// If there is more work to do in this returnFiber, do that next.workInProgress = siblingFiber;return;} // Otherwise, return to the parentcompletedWork = returnFiber; // Update the next thing we're working on in case something throws.workInProgress = completedWork;} while (completedWork !== null); // We've reached the root.if (workInProgressRootExitStatus === RootInProgress) {workInProgressRootExitStatus = RootCompleted;}
}