执行工作单元performUnitOfWork
其函数原型为
javascript
function performUnitOfWork(unitOfWork: Fiber): void
其步骤为
- 先执行当前的工作单元beginWork,返回下一执行单元(一般为子节点)
- 如果没有下一执行单元(即没有子节点),则表示当前单元执行完completeUnitOfWork
- completeUnitOfWork会更新当前的处理单元,如果是子节点,则看兄弟节点sibling,如果没有兄弟节点,则看父节点(return)
javascript
function performUnitOfWork(unitOfWork: Fiber): void {
// 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.
const current = unitOfWork.alternate;
let next;
if (enableProfilerTimer && (unitOfWork.mode & ProfileMode) !== NoMode) {
startProfilerTimer(unitOfWork);
if (__DEV__) {
next = runWithFiberInDEV(
unitOfWork,
beginWork,
current,
unitOfWork,
entangledRenderLanes,
);
} else {
next = beginWork(current, unitOfWork, entangledRenderLanes);
}
stopProfilerTimerIfRunningAndRecordDuration(unitOfWork);
} else {
if (__DEV__) {
next = runWithFiberInDEV(
unitOfWork,
beginWork,
current,
unitOfWork,
entangledRenderLanes,
);
} else {
next = beginWork(current, unitOfWork, entangledRenderLanes);
}
}
unitOfWork.memoizedProps = unitOfWork.pendingProps;
if (next === null) {
// If this doesn't spawn new work, complete the current work.
completeUnitOfWork(unitOfWork);
} else {
workInProgress = next;
}
}
function beginWork(
current: Fiber | null,
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber | null {
if (__DEV__) {
if (workInProgress._debugNeedsRemount && current !== null) {
// This will restart the begin phase with a new fiber.
const copiedFiber = createFiberFromTypeAndProps(
workInProgress.type,
workInProgress.key,
workInProgress.pendingProps,
workInProgress._debugOwner || null,
workInProgress.mode,
workInProgress.lanes,
);
copiedFiber._debugStack = workInProgress._debugStack;
copiedFiber._debugTask = workInProgress._debugTask;
return remountFiber(current, workInProgress, copiedFiber);
}
}
if (current !== null) {
const oldProps = current.memoizedProps;
const newProps = workInProgress.pendingProps;
if (
oldProps !== newProps ||
hasLegacyContextChanged() ||
// Force a re-render if the implementation changed due to hot reload:
(__DEV__ ? workInProgress.type !== current.type : false)
) {
// 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.
const 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.
const slotIndex = workInProgress.index;
const numberOfForks = getForksAtLevel(workInProgress);
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 LazyComponent: {
const elementType = workInProgress.elementType;
return mountLazyComponent(
current,
workInProgress,
elementType,
renderLanes,
);
}
case FunctionComponent: {
const Component = workInProgress.type;
return updateFunctionComponent(
current,
workInProgress,
Component,
workInProgress.pendingProps,
renderLanes,
);
}
case ClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps = resolveClassComponentProps(
Component,
unresolvedProps,
);
return updateClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case HostRoot:
return updateHostRoot(current, workInProgress, renderLanes);
case HostHoistable:
if (supportsResources) {
return updateHostHoistable(current, workInProgress, renderLanes);
}
// Fall through
case HostSingleton:
if (supportsSingletons) {
return updateHostSingleton(current, workInProgress, renderLanes);
}
// Fall through
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: {
return updateForwardRef(
current,
workInProgress,
workInProgress.type,
workInProgress.pendingProps,
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: {
return updateMemoComponent(
current,
workInProgress,
workInProgress.type,
workInProgress.pendingProps,
renderLanes,
);
}
case SimpleMemoComponent: {
return updateSimpleMemoComponent(
current,
workInProgress,
workInProgress.type,
workInProgress.pendingProps,
renderLanes,
);
}
case IncompleteClassComponent: {
if (disableLegacyMode) {
break;
}
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps = resolveClassComponentProps(
Component,
unresolvedProps,
);
return mountIncompleteClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case IncompleteFunctionComponent: {
if (disableLegacyMode) {
break;
}
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps = resolveClassComponentProps(
Component,
unresolvedProps,
);
return mountIncompleteFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderLanes,
);
}
case SuspenseListComponent: {
return updateSuspenseListComponent(current, workInProgress, renderLanes);
}
case ScopeComponent: {
if (enableScopeAPI) {
return updateScopeComponent(current, workInProgress, renderLanes);
}
break;
}
case ActivityComponent: {
return updateActivityComponent(current, workInProgress, renderLanes);
}
case OffscreenComponent: {
return updateOffscreenComponent(
current,
workInProgress,
renderLanes,
workInProgress.pendingProps,
);
}
case LegacyHiddenComponent: {
if (enableLegacyHidden) {
return updateLegacyHiddenComponent(
current,
workInProgress,
renderLanes,
);
}
break;
}
case CacheComponent: {
return updateCacheComponent(current, workInProgress, renderLanes);
}
case TracingMarkerComponent: {
if (enableTransitionTracing) {
return updateTracingMarkerComponent(
current,
workInProgress,
renderLanes,
);
}
break;
}
case ViewTransitionComponent: {
if (enableViewTransition) {
return updateViewTransition(current, workInProgress, renderLanes);
}
break;
}
case Throw: {
// This represents a Component that threw in the reconciliation phase.
// So we'll rethrow here. This might be a Thenable.
throw workInProgress.pendingProps;
}
}
throw new Error(
`Unknown unit of work tag (${workInProgress.tag}). This error is likely caused by a bug in ` +
'React. Please file an issue.',
);
}
function completeUnitOfWork(unitOfWork: Fiber): void {
// 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.
let completedWork: Fiber = unitOfWork;
do {
if ((completedWork.flags & Incomplete) !== NoFlags) {
// This fiber did not complete, because one of its children did not
// complete. Switch to unwinding the stack instead of completing it.
//
// The reason "unwind" and "complete" is interleaved is because when
// something suspends, we continue rendering the siblings even though
// they will be replaced by a fallback.
const skipSiblings = workInProgressRootDidSkipSuspendedSiblings;
unwindUnitOfWork(completedWork, skipSiblings);
return;
}
// 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.
const current = completedWork.alternate;
const returnFiber = completedWork.return;
let next;
startProfilerTimer(completedWork);
if (__DEV__) {
next = runWithFiberInDEV(
completedWork,
completeWork,
current,
completedWork,
entangledRenderLanes,
);
} else {
next = completeWork(current, completedWork, entangledRenderLanes);
}
if (enableProfilerTimer && (completedWork.mode & ProfileMode) !== NoMode) {
// Update render duration assuming we didn't error.
stopProfilerTimerIfRunningAndRecordIncompleteDuration(completedWork);
}
if (next !== null) {
// Completing this fiber spawned new work. Work on that next.
workInProgress = next;
return;
}
const 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 parent
// $FlowFixMe[incompatible-type] we bail out when we get a null
completedWork = 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;
}
}
beginWork根据tag作不同的处理
| tag | 处理 |
|---|---|
| LazyComponent | mountLazyComponent |
| FunctionComponent | updateFunctionComponent |
| ClassComponent | updateClassComponent |
| HostRoot | updateHostRoot |
| HostHoistable | updateHostHoistable |
| HostSingleton | updateHostSingleton |
| HostComponent | updateHostComponent |
| HostText | updateHostText |
| SuspenseComponent | updateSuspenseComponent |
| HostPortal | updatePortalComponent |
| ForwardRef | updateForwardRef |
| Fragment | updateFragment |
| Mode | updateMode |
| Profiler | updateProfiler |
| ContextProvider | updateContextProvider |
| ContextConsumer | updateContextConsumer |
| MemoComponent | updateMemoComponent |
| SimpleMemoComponent | updateSimpleMemoComponent |
| IncompleteClassComponent | mountIncompleteClassComponent |
| IncompleteFunctionComponent | mountIncompleteFunctionComponent |
| SuspenseListComponent | updateSuspenseListComponent |
| ScopeComponent | updateScopeComponent |
| ActivityComponent | updateActivityComponent |
| OffscreenComponent | updateOffscreenComponent |
| LegacyHiddenComponent | updateLegacyHiddenComponent |
| CacheComponent | updateCacheComponent |
| TracingMarkerComponent | updateTracingMarkerComponent |
| ViewTransitionComponent | updateViewTransition |