react源码学习之reconcile

张开发
2026/4/10 1:38:44 15 分钟阅读

分享文章

react源码学习之reconcile
执行工作单元performUnitOfWork其函数原型为functionperformUnitOfWork(unitOfWork:Fiber):void其步骤为先执行当前的工作单元beginWork返回下一执行单元一般为子节点如果没有下一执行单元即没有子节点则表示当前单元执行完completeUnitOfWorkcompleteUnitOfWork会更新当前的处理单元如果是子节点则看兄弟节点sibling如果没有兄弟节点则看父节点(return)functionperformUnitOfWork(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 dont// need an additional field on the work in progress.constcurrentunitOfWork.alternate;letnext;if(enableProfilerTimer(unitOfWork.modeProfileMode)!NoMode){startProfilerTimer(unitOfWork);if(__DEV__){nextrunWithFiberInDEV(unitOfWork,beginWork,current,unitOfWork,entangledRenderLanes,);}else{nextbeginWork(current,unitOfWork,entangledRenderLanes);}stopProfilerTimerIfRunningAndRecordDuration(unitOfWork);}else{if(__DEV__){nextrunWithFiberInDEV(unitOfWork,beginWork,current,unitOfWork,entangledRenderLanes,);}else{nextbeginWork(current,unitOfWork,entangledRenderLanes);}}unitOfWork.memoizedPropsunitOfWork.pendingProps;if(nextnull){// If this doesnt spawn new work, complete the current work.completeUnitOfWork(unitOfWork);}else{workInProgressnext;}}functionbeginWork(current:Fiber|null,workInProgress:Fiber,renderLanes:Lanes,):Fiber|null{if(__DEV__){if(workInProgress._debugNeedsRemountcurrent!null){// This will restart the begin phase with a new fiber.constcopiedFibercreateFiberFromTypeAndProps(workInProgress.type,workInProgress.key,workInProgress.pendingProps,workInProgress._debugOwner||null,workInProgress.mode,workInProgress.lanes,);copiedFiber._debugStackworkInProgress._debugStack;copiedFiber._debugTaskworkInProgress._debugTask;returnremountFiber(current,workInProgress,copiedFiber);}}if(current!null){constoldPropscurrent.memoizedProps;constnewPropsworkInProgress.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).didReceiveUpdatetrue;}else{// Neither props nor legacy context changes. Check if theres a pending// update or context change.consthasScheduledUpdateOrContextcheckScheduledUpdateOrContext(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.flagsDidCapture)NoFlags){// No pending updates or context. Bail out now.didReceiveUpdatefalse;returnattemptEarlyBailoutIfNoScheduledUpdate(current,workInProgress,renderLanes,);}if((current.flagsForceUpdateForLegacySuspense)!NoFlags){// This is a special case that only exists for legacy mode.// See https://github.com/facebook/react/pull/19216.didReceiveUpdatetrue;}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.didReceiveUpdatefalse;}}}else{didReceiveUpdatefalse;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.constslotIndexworkInProgress.index;constnumberOfForksgetForksAtLevel(workInProgress);pushTreeId(workInProgress,numberOfForks,slotIndex);}}// Before entering the begin phase, clear pending update priority.// TODO: This assumes that were about to evaluate the component and process// the update queue. However, theres 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.lanesNoLanes;switch(workInProgress.tag){caseLazyComponent:{constelementTypeworkInProgress.elementType;returnmountLazyComponent(current,workInProgress,elementType,renderLanes,);}caseFunctionComponent:{constComponentworkInProgress.type;returnupdateFunctionComponent(current,workInProgress,Component,workInProgress.pendingProps,renderLanes,);}caseClassComponent:{constComponentworkInProgress.type;constunresolvedPropsworkInProgress.pendingProps;constresolvedPropsresolveClassComponentProps(Component,unresolvedProps,);returnupdateClassComponent(current,workInProgress,Component,resolvedProps,renderLanes,);}caseHostRoot:returnupdateHostRoot(current,workInProgress,renderLanes);caseHostHoistable:if(supportsResources){returnupdateHostHoistable(current,workInProgress,renderLanes);}// Fall throughcaseHostSingleton:if(supportsSingletons){returnupdateHostSingleton(current,workInProgress,renderLanes);}// Fall throughcaseHostComponent:returnupdateHostComponent(current,workInProgress,renderLanes);caseHostText:returnupdateHostText(current,workInProgress);caseSuspenseComponent:returnupdateSuspenseComponent(current,workInProgress,renderLanes);caseHostPortal:returnupdatePortalComponent(current,workInProgress,renderLanes);caseForwardRef:{returnupdateForwardRef(current,workInProgress,workInProgress.type,workInProgress.pendingProps,renderLanes,);}caseFragment:returnupdateFragment(current,workInProgress,renderLanes);caseMode:returnupdateMode(current,workInProgress,renderLanes);caseProfiler:returnupdateProfiler(current,workInProgress,renderLanes);caseContextProvider:returnupdateContextProvider(current,workInProgress,renderLanes);caseContextConsumer:returnupdateContextConsumer(current,workInProgress,renderLanes);caseMemoComponent:{returnupdateMemoComponent(current,workInProgress,workInProgress.type,workInProgress.pendingProps,renderLanes,);}caseSimpleMemoComponent:{returnupdateSimpleMemoComponent(current,workInProgress,workInProgress.type,workInProgress.pendingProps,renderLanes,);}caseIncompleteClassComponent:{if(disableLegacyMode){break;}constComponentworkInProgress.type;constunresolvedPropsworkInProgress.pendingProps;constresolvedPropsresolveClassComponentProps(Component,unresolvedProps,);returnmountIncompleteClassComponent(current,workInProgress,Component,resolvedProps,renderLanes,);}caseIncompleteFunctionComponent:{if(disableLegacyMode){break;}constComponentworkInProgress.type;constunresolvedPropsworkInProgress.pendingProps;constresolvedPropsresolveClassComponentProps(Component,unresolvedProps,);returnmountIncompleteFunctionComponent(current,workInProgress,Component,resolvedProps,renderLanes,);}caseSuspenseListComponent:{returnupdateSuspenseListComponent(current,workInProgress,renderLanes);}caseScopeComponent:{if(enableScopeAPI){returnupdateScopeComponent(current,workInProgress,renderLanes);}break;}caseActivityComponent:{returnupdateActivityComponent(current,workInProgress,renderLanes);}caseOffscreenComponent:{returnupdateOffscreenComponent(current,workInProgress,renderLanes,workInProgress.pendingProps,);}caseLegacyHiddenComponent:{if(enableLegacyHidden){returnupdateLegacyHiddenComponent(current,workInProgress,renderLanes,);}break;}caseCacheComponent:{returnupdateCacheComponent(current,workInProgress,renderLanes);}caseTracingMarkerComponent:{if(enableTransitionTracing){returnupdateTracingMarkerComponent(current,workInProgress,renderLanes,);}break;}caseViewTransitionComponent:{if(enableViewTransition){returnupdateViewTransition(current,workInProgress,renderLanes);}break;}caseThrow:{// This represents a Component that threw in the reconciliation phase.// So well rethrow here. This might be a Thenable.throwworkInProgress.pendingProps;}}thrownewError(Unknown unit of work tag (${workInProgress.tag}). This error is likely caused by a bug inReact. Please file an issue.,);}functioncompleteUnitOfWork(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.letcompletedWork:FiberunitOfWork;do{if((completedWork.flagsIncomplete)!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.constskipSiblingsworkInProgressRootDidSkipSuspendedSiblings;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 dont// need an additional field on the work in progress.constcurrentcompletedWork.alternate;constreturnFibercompletedWork.return;letnext;startProfilerTimer(completedWork);if(__DEV__){nextrunWithFiberInDEV(completedWork,completeWork,current,completedWork,entangledRenderLanes,);}else{nextcompleteWork(current,completedWork,entangledRenderLanes);}if(enableProfilerTimer(completedWork.modeProfileMode)!NoMode){// Update render duration assuming we didnt error.stopProfilerTimerIfRunningAndRecordIncompleteDuration(completedWork);}if(next!null){// Completing this fiber spawned new work. Work on that next.workInProgressnext;return;}constsiblingFibercompletedWork.sibling;if(siblingFiber!null){// If there is more work to do in this returnFiber, do that next.workInProgresssiblingFiber;return;}// Otherwise, return to the parent// $FlowFixMe[incompatible-type] we bail out when we get a nullcompletedWorkreturnFiber;// Update the next thing were working on in case something throws.workInProgresscompletedWork;}while(completedWork!null);// Weve reached the root.if(workInProgressRootExitStatusRootInProgress){workInProgressRootExitStatusRootCompleted;}}

更多文章