




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第react源碼層分析協(xié)調(diào)與調(diào)度目錄requestEventTimerequestUpdateLanefindUpdateLanelanePriorityLanePrioritycreateUpdateenqueueUpdate總結(jié)協(xié)調(diào)與調(diào)度reconciler流程同步任務(wù)類型執(zhí)行機(jī)制異步任務(wù)類型執(zhí)行機(jī)制shouldYieldperformUnitOfWorkbeginWorkcompleteUnitOfWorkscheduler流程performWorkUntilDeadline總結(jié)
requestEventTime
其實(shí)在React執(zhí)行過(guò)程中,會(huì)有數(shù)不清的任務(wù)要去執(zhí)行,但是他們會(huì)有一個(gè)優(yōu)先級(jí)的判定,假如兩個(gè)事件的優(yōu)先級(jí)一樣,那么React是怎么去判定他們兩誰(shuí)先執(zhí)行呢?
//packages/react-reconciler/src/ReactFiberWorkLoop.old.js
exportfunctionrequestEventTime(){
if((executionContext(RenderContext|CommitContext))!==NoContext){
//We'reinsideReact,soit'sfinetoreadtheactualtime.
//react事件正在執(zhí)行
//executionContext
//RenderContext正在計(jì)算
//CommitContext正在提交
//exportconstNoContext=/**/0b0000000;
//constBatchedContext=/**/0b0000001;
//constEventContext=/**/0b0000010;
//constDiscreteEventContext=/**/0b0000100;
//constLegacyUnbatchedContext=/**/0b0001000;
//constRenderContext=/**/0b0010000;
//constCommitContext=/**/0b0100000;
//exportconstRetryAfterError=/**/0b1000000;
returnnow();
//沒有在react事件執(zhí)行NoTimestamp===-1
if(currentEventTime!==NoTimestamp){
//瀏覽器事件正在執(zhí)行,返回上次的currentEventTime
returncurrentEventTime;
//重新計(jì)算currentEventTime,當(dāng)執(zhí)行被中斷后
currentEventTime=now();
returncurrentEventTime;
}
RenderContext與CommitContext表示正在計(jì)算更新和正在提交更新,返回now()。如果是瀏覽器事件正在執(zhí)行中,返回上一次的currentEventTime。如果終止或者中斷react任務(wù)執(zhí)行的時(shí)候,則重新獲取執(zhí)行時(shí)間now()。獲取的時(shí)間越小,則執(zhí)行的優(yōu)先級(jí)越高。
now()并不是單純的newDate(),而是判定兩次更新任務(wù)的時(shí)間是否小于10ms,來(lái)決定是否復(fù)用上一次的更新時(shí)間Scheduler_now的。
exportconstnow=initialTimeMs10000Scheduler_now:()=Scheduler_now()-initialTimeMs;
其實(shí)各位猜想一下,對(duì)于10ms級(jí)別的任務(wù)間隙時(shí)間,幾乎是可以忽略不計(jì)的,那么這里就可以視為同樣的任務(wù),不需要有很大的性能開銷,有利于批量更新。
requestUpdateLane
requestEventTime位每一個(gè)需要執(zhí)行的任務(wù)打上了觸發(fā)更新時(shí)間標(biāo)簽,那么任務(wù)的優(yōu)先級(jí)還需要進(jìn)一步的確立,requestUpdateLane就是用來(lái)獲取每一個(gè)任務(wù)執(zhí)行的優(yōu)先級(jí)的。
//packages/react-reconciler/src/ReactFiberWorkLoop.old.js
exportfunctionrequestUpdateLane(fiber:Fiber):Lane{
//Specialcases
constmode=fiber.mode;
if((modeBlockingMode)===NoMode){
return(SyncLane:Lane);
}elseif((modeConcurrentMode)===NoMode){
returngetCurrentPriorityLevel()===ImmediateSchedulerPriority
(SyncLane:Lane)
:(SyncBatchedLane:Lane);
}elseif(
!deferRenderPhaseUpdateToNextBatch
(executionContextRenderContext)!==NoContext
workInProgressRootRenderLanes!==NoLanes
//Thisisarenderphaseupdate.Thesearenotofficiallysupported.The
//oldbehavioristogivethisthesame"thread"(expirationtime)as
//whateveriscurrentlyrendering.Soifyoucall`setState`onacomponent
//thathappenslaterinthesamerender,itwillflush.Ideally,wewantto
//removethespecialcaseandtreatthemasiftheycamefroman
//interleavedevent.Regardless,thispatternisnotofficiallysupported.
//Thisbehaviorisonlyafallback.Theflagonlyexistsuntilwecanroll
//outthesetStatewarning,sinceexistingcodemightaccidentallyrelyon
//thecurrentbehavior.
returnpickArbitraryLane(workInProgressRootRenderLanes);
//Thealgorithmforassigninganupdatetoalaneshouldbestableforall
//updatesatthesameprioritywithinthesameevent.Todothis,theinputs
//tothealgorithmmustbethesame.Forexample,weusethe`renderLanes`
//toavoidchoosingalanethatisalreadyinthemiddleofrendering.
//However,the"included"lanescouldbemutatedinbetweenupdatesinthe
//sameevent,likeifyouperformanupdateinside`flushSync`.Oranyother
//codepaththatmightcall`prepareFreshStack`.
//Thetrickweuseistocachethefirstofeachoftheseinputswithinan
//event.Thenresetthecachedvaluesoncewecanbesuretheeventisover.
//Ourheuristicforthatiswheneverweenteraconcurrentworkloop.
//We'lldothesamefor`currentEventPendingLanes`below.
if(currentEventWipLanes===NoLanes){
currentEventWipLanes=workInProgressRootIncludedLanes;
constisTransition=requestCurrentTransition()!==NoTransition;
if(isTransition){
if(currentEventPendingLanes!==NoLanes){
currentEventPendingLanes=
mostRecentlyUpdatedRoot!==null
mostRecentlyUpdatedRoot.pendingLanes
:NoLanes;
returnfindTransitionLane(currentEventWipLanes,currentEventPendingLanes);
//TODO:RemovethisdependencyontheSchedulerpriority.
//Todothat,we'rereplacingitwithanupdatelanepriority.
//獲取執(zhí)行任務(wù)的優(yōu)先級(jí),便于調(diào)度
constschedulerPriority=getCurrentPriorityLevel();
//TheoldbehaviorwasusingthepriorityleveloftheScheduler.
//ThiscouplesReacttotheSchedulerinternals,sowe'rereplacingit
//withthecurrentUpdateLanePriorityabove.Asanexampleofhowthis
//couldbeproblematic,ifwe'renotinside`Scheduler.runWithPriority`,
//thenwe'llgetthepriorityofthecurrentrunningSchedulertask,
//whichisprobablynotwhatwewant.
letlane;
if(
//TODO:Temporary.We'reremovingtheconceptofdiscreteupdates.
(executionContextDiscreteEventContext)!==NoContext
//用戶block的類型事件
schedulerPriority===UserBlockingSchedulerPriority
//通過(guò)findUpdateLane函數(shù)重新計(jì)算lane
lane=findUpdateLane(InputDiscreteLanePriority,currentEventWipLanes);
}else{
//根據(jù)優(yōu)先級(jí)計(jì)算法則計(jì)算lane
constschedulerLanePriority=schedulerPriorityToLanePriority(
schedulerPriority,
if(decoupleUpdatePriorityFromScheduler){
//Inthenewstrategy,wewilltrackthecurrentupdatelanepriority
//insideReactandusethatprioritytoselectalaneforthisupdate.
//Fornow,we'rejustloggingwhenthey'redifferentsowecanassess.
constcurrentUpdateLanePriority=getCurrentUpdateLanePriority();
if(
schedulerLanePriority!==currentUpdateLanePriority
currentUpdateLanePriority!==NoLanePriority
if(__DEV__){
console.error(
'Expectedcurrentschedulerlanepriority%stomatchcurrentupdatelanepriority%s',
schedulerLanePriority,
currentUpdateLanePriority,
//根據(jù)計(jì)算得到的schedulerLanePriority,計(jì)算更新的優(yōu)先級(jí)lane
lane=findUpdateLane(schedulerLanePriority,currentEventWipLanes);
returnlane;
}
通過(guò)getCurrentPriorityLevel獲得所有執(zhí)行任務(wù)的調(diào)度優(yōu)先級(jí)schedulerPriority。通過(guò)findUpdateLane計(jì)算lane,作為更新中的優(yōu)先級(jí)。
findUpdateLane
exportfunctionfindUpdateLane(
lanePriority:LanePriority,wipLanes:Lanes,
):Lane{
switch(lanePriority){
caseNoLanePriority:
break;
caseSyncLanePriority:
returnSyncLane;
caseSyncBatchedLanePriority:
returnSyncBatchedLane;
caseInputDiscreteLanePriority:{
constlane=pickArbitraryLane(InputDiscreteLanes~wipLanes);
if(lane===NoLane){
//Shifttothenextprioritylevel
returnfindUpdateLane(InputContinuousLanePriority,wipLanes);
returnlane;
caseInputContinuousLanePriority:{
constlane=pickArbitraryLane(InputContinuousLanes~wipLanes);
if(lane===NoLane){
//Shifttothenextprioritylevel
returnfindUpdateLane(DefaultLanePriority,wipLanes);
returnlane;
caseDefaultLanePriority:{
letlane=pickArbitraryLane(DefaultLanes~wipLanes);
if(lane===NoLane){
//Ifallthedefaultlanesarealreadybeingworkedon,lookfora
//laneinthetransitionrange.
lane=pickArbitraryLane(TransitionLanes~wipLanes);
if(lane===NoLane){
//Allthetransitionlanesaretaken,too.Thisshouldbevery
//rare,butasalastresort,pickadefaultlane.Thiswillhave
//theeffectofinterruptingthecurrentwork-in-progressrender.
lane=pickArbitraryLane(DefaultLanes);
returnlane;
caseTransitionPriority://ShouldbehandledbyfindTransitionLaneinstead
caseRetryLanePriority://ShouldbehandledbyfindRetryLaneinstead
break;
caseIdleLanePriority:
letlane=pickArbitraryLane(IdleLanes~wipLanes);
if(lane===NoLane){
lane=pickArbitraryLane(IdleLanes);
returnlane;
default:
//Theremainingprioritiesarenotvalidforupdates
break;
invariant(
false,
'Invalidupdatepriority:%s.ThisisabuginReact.',
lanePriority,
}
相關(guān)參考視頻講解:進(jìn)入學(xué)習(xí)
lanePriorityLanePriority
exportopaquetypeLanePriority=
|10
|11
|12
|13
|14
|15
|16
|17;
exportopaquetypeLanes=number;
exportopaquetypeLane=number;
exportopaquetypeLaneMapT=ArrayT
import{
ImmediatePriorityasImmediateSchedulerPriority,
UserBlockingPriorityasUserBlockingSchedulerPriority,
NormalPriorityasNormalSchedulerPriority,
LowPriorityasLowSchedulerPriority,
IdlePriorityasIdleSchedulerPriority,
NoPriorityasNoSchedulerPriority,
}from'./SchedulerWithReactIntegration.new';
//同步任務(wù)
exportconstSyncLanePriority:LanePriority=15;
exportconstSyncBatchedLanePriority:LanePriority=14;
//用戶事件
constInputDiscreteHydrationLanePriority:LanePriority=13;
exportconstInputDiscreteLanePriority:LanePriority=12;
constInputContinuousHydrationLanePriority:LanePriority=11;
exportconstInputContinuousLanePriority:LanePriority=10;
constDefaultHydrationLanePriority:LanePriority=9;
exportconstDefaultLanePriority:LanePriority=8;
constTransitionHydrationPriority:LanePriority=7;
exportconstTransitionPriority:LanePriority=6;
constRetryLanePriority:LanePriority=5;
constSelectiveHydrationLanePriority:LanePriority=4;
constIdleHydrationLanePriority:LanePriority=3;
constIdleLanePriority:LanePriority=2;
constOffscreenLanePriority:LanePriority=1;
exportconstNoLanePriority:LanePriority=0;
createUpdate
exportfunctioncreateUpdate(eventTime:number,lane:Lane):Update*{
constupdate:Update*={
eventTime,//更新時(shí)間
lane,//優(yōu)先級(jí)
tag:UpdateState,//更新
payload:null,//需要更新的內(nèi)容
callback:null,//更新完后的回調(diào)
next:null,//指向下一個(gè)更新
returnupdate;
}
createUpdate函數(shù)入?yún)閑ventTime和lane,輸出一個(gè)update對(duì)象,而對(duì)象中的tag表示此對(duì)象要進(jìn)行什么樣的操作。
exportconstUpdateState=0;//更新
exportconstReplaceState=1;//替換
exportconstForceUpdate=2;//強(qiáng)制更新
exportconstCaptureUpdate=3;//xx更新
createUpdate就是單純的給每一個(gè)任務(wù)進(jìn)行包裝,作為一個(gè)個(gè)體推入到更新隊(duì)列中。
enqueueUpdate
exportfunctionenqueueUpdateState(fiber:Fiber,update:UpdateState){
//獲取當(dāng)前更新隊(duì)列?為啥呢?因?yàn)闊o(wú)法保證react是不是還有正在更新或者沒有更新完畢的任務(wù)
constupdateQueue=fiber.updateQueue;
//如果更新隊(duì)列為空,則表示fiber還未渲染,直接退出
if(updateQueue===null){
//Onlyoccursifthefiberhasbeenunmounted.
return;
constsharedQueue:SharedQueueState=(updateQueue:any).shared;
constpending=sharedQueue.pending;
if(pending===null){
//Thisisthefirstupdate.Createacircularlist.
//還記得那個(gè)更新對(duì)象嗎?update.next=
//如果pedding位null,表示第一次渲染,那么他的指針為update本身
update.next=update;
}else{
//將update插入到更新隊(duì)列循環(huán)當(dāng)中
update.next=pending.next;
pending.next=update;
sharedQueue.pending=update;
if(__DEV__){
if(
currentlyProcessingQueue===sharedQueue
!didWarnUpdateInsideUpdate
console.error(
'Anupdate(setState,replaceState,orforceUpdate)wasscheduled'+
'frominsideanupdatefunction.Updatefunctionsshouldbepure,'+
'withzeroside-effects.ConsiderusingcomponentDidUpdateora'+
'callback.',
didWarnUpdateInsideUpdate=true;
}
這一步就是把需要更新的對(duì)象,與fiber更新隊(duì)列關(guān)聯(lián)起來(lái)。
總結(jié)
React通過(guò)獲取事件的優(yōu)先級(jí),處理具有同樣優(yōu)先級(jí)的事件,創(chuàng)建更新對(duì)象并與fiber的更新隊(duì)列關(guān)聯(lián)起來(lái)。到這一步updateContainer這個(gè)流程就走完了,也下面就是開始他的協(xié)調(diào)階段了。
協(xié)調(diào)與調(diào)度
協(xié)調(diào)與調(diào)度的流程大致如圖所示:
reconciler流程
React的reconciler流程以scheduleUpdateOnFiber為入口,并在checkForNestedUpdates里面處理任務(wù)更新的嵌套層數(shù),如果嵌套層數(shù)過(guò)大(50),就會(huì)認(rèn)為是無(wú)效更新,則會(huì)拋出異常。之后便根據(jù)markUpdateLaneFromFiberToRoot對(duì)當(dāng)前的fiber樹,自底向上的遞歸fiber的lane,根據(jù)lane做二進(jìn)制比較或者位運(yùn)算處理。詳情如下:
如果當(dāng)前執(zhí)行任務(wù)的優(yōu)先級(jí)為同步,則去判斷有無(wú)正在執(zhí)行的React任務(wù)。如果沒有則執(zhí)行ensureRootIsScheduled,進(jìn)行調(diào)度處理。如果當(dāng)前任務(wù)優(yōu)先級(jí)是異步執(zhí)行,則執(zhí)行ensureRootIsScheduled進(jìn)行調(diào)度處理。
exportfunctionscheduleUpdateOnFiber(
fiber:Fiber,lane:Lane,eventTime:number,
//檢查嵌套層數(shù),避免是循環(huán)做無(wú)效操作
checkForNestedUpdates();
warnAboutRenderPhaseUpdatesInDEV(fiber);
//更新當(dāng)前更新隊(duì)列里面的任務(wù)優(yōu)先級(jí),自底而上更新child.fiberLanes
constroot=markUpdateLaneFromFiberToRoot(fiber,lane);
if(root===null){
warnAboutUpdateOnUnmountedFiberInDEV(fiber);
returnnull;
//Markthattheroothasapendingupdate.
//標(biāo)記root有更新的,執(zhí)行它
markRootUpdated(root,lane,eventTime);
if(root===workInProgressRoot){
//Receivedanupdatetoatreethat'sinthemiddleofrendering.Mark
//thattherewasaninterleavedupdateworkonthisroot.Unlessthe
//`deferRenderPhaseUpdateToNextBatch`flagisoffandthisisarender
//phaseupdate.Inthatcase,wedon'ttreatrenderphaseupdatesasif
//theywereinterleaved,forbackwardscompatreasons.
if(
deferRenderPhaseUpdateToNextBatch||
(executionContextRenderContext)===NoContext
workInProgressRootUpdatedLanes=mergeLanes(
workInProgressRootUpdatedLanes,
lane,
if(workInProgressRootExitStatus===RootSuspendedWithDelay){
//Therootalreadysuspendedwithadelay,whichmeansthisrender
//definitelywon'tfinish.Sincewehaveanewupdate,let'smarkitas
//suspendednow,rightbeforemarkingtheincomingupdate.Thishasthe
//effectofinterruptingthecurrentrenderandswitchingtotheupdate.
//TODO:Makesurethisdoesn'toverridepingsthathappenwhilewe've
//alreadystartedrendering.
markRootSuspended(root,workInProgressRootRenderLanes);
//TODO:requestUpdateLanePriorityalsoreadsthepriority.Passthe
//priorityasanargumenttothatfunctionandthisone.
//獲取當(dāng)前優(yōu)先級(jí)層次
constpriorityLevel=getCurrentPriorityLevel();
//同步任務(wù),采用同步更新的方式
if(lane===SyncLane){
if(
//Checkifwe'reinsideunbatchedUpdates
(executionContextLegacyUnbatchedContext)!==NoContext
//Checkifwe'renotalreadyrendering
(executionContext(RenderContext|CommitContext))===NoContext
//Registerpendinginteractionsontheroottoavoidlosingtracedinteractiondata.
//同步而且沒有react任務(wù)在執(zhí)行,調(diào)用performSyncWorkOnRoot
schedulePendingInteractions(root,lane);
//Thisisalegacyedgecase.TheinitialmountofaReactDOM.render-ed
//rootinsideofbatchedUpdatesshouldbesynchronous,butlayoutupdates
//shouldbedeferreduntiltheendofthebatch.
performSyncWorkOnRoot(root);
}else{
//如果有正在執(zhí)行的react任務(wù),那么執(zhí)行它ensureRootIsScheduled去復(fù)用當(dāng)前正在執(zhí)行的任務(wù)
//跟本次更新一起進(jìn)行
ensureRootIsScheduled(root,eventTime);
schedulePendingInteractions(root,lane);
if(executionContext===NoContext){
//Flushthesynchronousworknow,unlesswe'realreadyworkingorinside
//abatch.ThisisintentionallyinsidescheduleUpdateOnFiberinsteadof
//scheduleCallbackForFibertopreservetheabilitytoscheduleacallback
//withoutimmediatelyflushingit.Weonlydothisforuser-initiated
//updates,topreservehistoricalbehavioroflegacymode.
resetRenderTimer();
flushSyncCallbackQueue();
}else{
//Scheduleadiscreteupdatebutonlyifit'snotSync.
//如果此次是異步任務(wù)
if(
(executionContextDiscreteEventContext)!==NoContext
//Onlyupdatesatuser-blockingpriorityorgreaterareconsidered
//discrete,eveninsideadiscreteevent.
(priorityLevel===UserBlockingSchedulerPriority||
priorityLevel===ImmediateSchedulerPriority)
//Thisistheresultofadiscreteevent.Trackthelowestpriority
//discreteupdateperrootsowecanflushthemearly,ifneeded.
if(rootsWithPendingDiscreteUpdates===null){
rootsWithPendingDiscreteUpdates=newSet([root]);
}else{
rootsWithPendingDiscreteUpdates.add(root);
//Scheduleotherupdatesafterincasethecallbackissync.
//可以中斷更新,只要調(diào)用ensureRootIsScheduled=performConcurrentWorkOnRoot
ensureRootIsScheduled(root,eventTime);
schedulePendingInteractions(root,lane);
//Weusethiswhenassigningalaneforatransitioninside
//`requestUpdateLane`.Weassumeit'sthesameastherootbeingupdated,
//sinceinthecommoncaseofasinglerootappitprobablyis.Ifit'snot
//thesameroot,thenit'snotahugedeal,wejustmightbatchmorestuff
//togethermorethannecessary.
mostRecentlyUpdatedRoot=root;
}
同步任務(wù)類型執(zhí)行機(jī)制
當(dāng)任務(wù)的類型為同步任務(wù),并且當(dāng)前的js主線程空閑,會(huì)通過(guò)performSyncWorkOnRoot(root)方法開始執(zhí)行同步任務(wù)。
performSyncWorkOnRoot里面主要做了兩件事:
renderRootSync從根節(jié)點(diǎn)開始進(jìn)行同步渲染任務(wù)commitRoot執(zhí)行commit流程
當(dāng)前js線程中有正在執(zhí)行的任務(wù)時(shí)候,就會(huì)觸發(fā)ensureRootIsScheduled函數(shù)。ensureRootIsScheduled里面主要是處理當(dāng)前加入的更新任務(wù)的lane是否有變化:
如果沒有變化則表示跟當(dāng)前的schedule一起執(zhí)行。如果有則創(chuàng)建新的schedule。調(diào)用performSyncWorkOnRoot執(zhí)行同步任務(wù)。
functionensureRootIsScheduled(root:FiberRoot,currentTime:number){
constexistingCallbackNode=root.callbackNode;
//Checkifanylanesarebeingstarvedbyotherwork.Ifso,markthemas
//expiredsoweknowtoworkonthosenext.
markStarvedLanesAsExpired(root,currentTime);
//Determinethenextlanestoworkon,andtheirpriority.
constnextLanes=getNextLanes(
root,
root===workInProgressRootworkInProgressRootRenderLanes:NoLanes,
//Thisreturnstheprioritylevelcomputedduringthe`getNextLanes`call.
constnewCallbackPriority=returnNextLanesPriority();
if(nextLanes===NoLanes){
//Specialcase:There'snothingtoworkon.
if(existingCallbackNode!==null){
cancelCallback(existingCallbackNode);
root.callbackNode=null;
root.callbackPriority=NoLanePriority;
return;
//Checkifthere'sanexistingtask.Wemaybeabletoreuseit.
if(existingCallbackNode!==null){
constexistingCallbackPriority=root.callbackPriority;
if(existingCallbackPriority===newCallbackPriority){
//Thepriorityhasn'tchanged.Wecanreusetheexistingtask.Exit.
return;
//Theprioritychanged.Canceltheexistingcallback.We'llscheduleanew
//onebelow.
cancelCallback(existingCallbackNode);
//Scheduleanewcallback.
letnewCallbackNode;
if(newCallbackPriority===SyncLanePriority){
//Specialcase:SyncReactcallbacksarescheduledonaspecial
//internalqueue
//同步任務(wù)調(diào)用performSyncWorkOnRoot
newCallbackNode=scheduleSyncCallback(
performSyncWorkOnRoot.bind(null,root),
}elseif(newCallbackPriority===SyncBatchedLanePriority){
newCallbackNode=scheduleCallback(
ImmediateSchedulerPriority,
performSyncWorkOnRoot.bind(null,root),
}else{
//異步任務(wù)調(diào)用performConcurrentWorkOnRoot
constschedulerPriorityLevel=lanePriorityToSchedulerPriority(
newCallbackPriority,
newCallbackNode=scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null,root),
root.callbackPriority=newCallbackPriority;
root.callbackNode=newCallbackNode;
}
所以任務(wù)類型為同步的時(shí)候,不管js線程空閑與否,都會(huì)走到performSyncWorkOnRoot,進(jìn)而走renderRootSync、workLoopSync流程,而在workLoopSync中,只要workInProgressfiber不為null,則會(huì)一直循環(huán)執(zhí)行performUnitOfWork,而performUnitOfWork中會(huì)去執(zhí)行beginWork和completeWork,也就是上一章里面說(shuō)的beginWork流程去創(chuàng)建每一個(gè)fiber節(jié)點(diǎn)
//packages/react-reconciler/src/ReactFiberWorkLoop.old.js
functionworkLoopSync(){
while(workInProgress!==null){
performUnitOfWork(workInProgress);
}
異步任務(wù)類型執(zhí)行機(jī)制
異步任務(wù)則會(huì)去執(zhí)行performConcurrentWorkOnRoot,進(jìn)而去執(zhí)行renderRootConcurrent、workLoopConcurrent,但是與同步任務(wù)不同的是異步任務(wù)是可以中斷的,這個(gè)可中斷的關(guān)鍵字就在于shouldYield,它本身返回值是一個(gè)false,為true則可以中斷。
//packages/react-reconciler/src/ReactFiberWorkLoop.old.js
functionworkLoopConcurrent(){
while(workInProgress!==null!shouldYield()){
performUnitOfWork(workInProgress);
}
每一次在執(zhí)行performUnitOfWork之前都會(huì)關(guān)注一下shouldYield()返回值,也就是說(shuō)的reconciler過(guò)程可中斷的意思。
shouldYield
//packages\scheduler\src\SchedulerPostTask.js
exportfunctionunstable_shouldYield(){
returngetCurrentTime()=deadline;
}
getCurrentTime為newDate(),deadline為瀏覽器處理每一幀結(jié)束時(shí)間戳,所以這里表示的是,在瀏覽器每一幀空閑的時(shí)候,才會(huì)去處理此任務(wù),如果當(dāng)前任務(wù)在瀏覽器執(zhí)行的某一幀里面,則會(huì)中斷當(dāng)前任務(wù),等待瀏覽器當(dāng)前幀執(zhí)行完畢,等到下一幀空閑的時(shí)候,才會(huì)去執(zhí)行當(dāng)前任務(wù)。
所以不管在workLoopConcurrent還是workLoopSync中,都會(huì)根據(jù)當(dāng)前的workInProgressfiber是否為null來(lái)進(jìn)行循環(huán)調(diào)用performUnitOfWork。根據(jù)流程圖以及上面說(shuō)的這一些,可以看得出來(lái)從beginWork到completeUnitOfWork這個(gè)過(guò)程究竟干了什么。
這三章將會(huì)講解fiber樹的reconcileChildren過(guò)程、completeWork過(guò)程、commitMutationEffectsinsertOrAppendPlacementNodeIntoContainer(DOM)過(guò)程。這里將詳細(xì)解讀v17版本的React的diff算法、虛擬dom到真實(shí)dom的創(chuàng)建,函數(shù)生命鉤子的執(zhí)行流程等。
performUnitOfWork
functionperformUnitOfWork(unitOfWork:Fiber):void{
//Thecurrent,flushed,stateofthisfiberisthealternate.Ideally
//nothingshouldrelyonthis,butrelyingonitheremeansthatwedon't
//needanadditionalfieldontheworkinprogress.
constcurrent=unitOfWork.alternate;
setCurrentDebugFiberInDEV(unitOfWork);
letnext;
if(enableProfilerTimer(unitOfWork.modeProfileMode)!==NoMode){
startProfilerTimer(unitOfWork);
next=beginWork(current,unitOfWork,subtreeRenderLanes);
stopProfilerTimerIfRunningAndRecordDelta(unitOfWork,true);
}else{
//beginWork
next=beginWork(current,unitOfWork,subtreeRenderLanes);
resetCurrentDebugFiberInDEV();
unitOfWork.memoizedProps=unitOfWork.pendingProps;
if(next===null){
//Ifthisdoesn'tspawnnewwork,completethecurrentwork.
//completeUnitOfWork
completeUnitOfWork(unitOfWork);
}else{
workInProgress=next;
ReactCurrentOwner.current=null;
}
所以在performUnitOfWork里面,每一次執(zhí)行beginWork,進(jìn)行workIngProgress更新,當(dāng)遍歷完畢整棵fiber樹之后便會(huì)執(zhí)行completeUnitOfWork。
beginWork
我們可以看到beginWork就是originBeginWork得實(shí)際執(zhí)行。我們翻開beginWork的源碼可以看到,它便是根據(jù)不同的workInProgress.tag執(zhí)行不同組件類型的處理函數(shù),這里就不去拆分的太細(xì),只有有想法便會(huì)單獨(dú)出一篇文章講述這個(gè)的細(xì)節(jié),但是最后都會(huì)去調(diào)用reconcileChildren。
completeUnitOfWork
當(dāng)遍歷完畢執(zhí)行beginWork,遍歷完畢之后就會(huì)走completeUnitOfWork。
functioncompleteUnitOfWork(unitOfWork:Fiber):void{
//Attempttocompletethecurrentunitofwork,thenmovetothenext
//sibling.Iftherearenomoresiblings,returntotheparentfiber.
letcompletedWork=unitOfWork;
do{
//Thecurrent,flushed,stateofthisfiberisthealternate.Ideally
//nothingshouldrelyonthis,butrelyingonitheremeansthatwedon't
//needanadditionalfieldontheworkinprogress.
constcurrent=completedWork.alternate;
constreturnFiber=completedWork.return;
//Checkiftheworkcompletedorifsomethingthrew.
if((completedWork.flagsIncomplete)===NoFlags){
setCurrentDebugFiberInDEV(completedWork);
letnext;
if(
!enableProfilerTimer||
(completedWork.modeProfileMode)===NoMode
//綁定事件,更新props,更新dom
next=completeWork(current,completedWork,subtreeRenderLanes);
}else{
startProfilerTimer(completedWork);
next=completeWork(current,completedWork,subtreeRenderLanes);
//Updaterenderdurationassumingwedidn'terror.
stopProfilerTimerIfRunningAndRecordDelta(completedWork,false);
resetCurrentDebugFiberInDEV();
if(next!==null){
//Completingthisfiberspawnednewwork.Workonthatnext.
workInProgress=next;
return;
resetChildLanes(completedWork);
if(
returnFiber!==null
//Donotappendeffectstoparentsifasiblingfailedtocomplete
(returnFiber.flagsIncomplete)===NoFlags
//Appendalltheeffectsofthesubtreeandthisfiberontotheeffect
//listoftheparent.Thecompletionorderofthechildrenaffectsthe
//side-effectorder.
//把已收集到的副作用,合并到父級(jí)effectlists中
if(returnFiber.firstEffect===null){
returnFiber.firstEffect=completedWork.firstEffect;
if(completedWork.lastEffect!==null){
if(returnFiber.lastEffect!==null){
returnFiber.lastEffect.nextEffect=completedWork.firstEffect;
returnFiber.lastEffect=completedWork.lastEffect;
//Ifthisfiberhadside-effects,weappenditAFTERthechildren's
//side-effects.Wecanperformcertainside-effectsearlierifneeded,
//bydoingmultiplepassesovertheeffectlist.Wedon'twantto
//scheduleourownside-effectonourownlistbecauseifendup
//reusingchildrenwe'llschedulethiseffectontoitselfsincewe're
//attheend.
constflags=completedWork.flags;
//SkipbothNoWorkandPerformedWorktagswhencreatingtheeffect
//list.PerformedWorkeffectisreadbyReactDevToolsbutshouldn'tbe
//committed.
//跳過(guò)NoWork,PerformedWork在commit階段用不到
if(flagsPerformedWork){
if(returnFiber.lastEffect!==null){
returnFiber.lastEffect.nextEffect=completedWork;
}else{
returnFiber.firstEffect=completedWork;
returnFiber.lastEffect=completedWork;
}else{
//Thisfiberdidnotcompletebecausesomethingthrew.Popvaluesoff
//thestackwithoutenteringthecompletephase.Ifthisisaboundary,
//capturevaluesifpossible.
constnext=unwindWork(completedWork,subtreeRenderLanes);
//Becausethisfiberdidnotcomplete,don'tresetitsexpirationtime.
if(next!==null){
//Ifcompletingthisworkspawnednewwork,dothatnext.We'llcome
//backhereagain.
//Sincewe'rerestarting,removeanythingthatisnotahosteffect
//fromtheeffecttag.
next.flags=HostEffectMask;
workInProgress=next;
return;
if(
enableProfilerTimer
(completedWork.modeProfileMode)!==NoMode
//Recordtherenderdurationforthefiberthaterrored.
stopProfilerTimerIfRunningAndRecordDelta(completedWork,false);
//Includethetimespentworkingonfailedchildrenbeforecontinuing.
letactualDuration=completedWork.actualDuration;
letchild=completedWork.child;
while(child!==null){
actualDuration+=child.actualDuration;
child=child.sibling;
completedWork.actualDuration=actualDuration;
if(returnFiber!==null){
//Marktheparentfiberasincompleteandclearitseffectlist.
returnFiber.firstEffect=returnFiber.lastEffect=null;
returnFiber.flags|=Incomplete;
//兄弟層指針
constsiblingFiber=completedWork.sibling;
if(siblingFiber!==null){
//IfthereismoreworktodointhisreturnFiber,dothatnext.
workInProgress=siblingFiber;
return;
//Otherwise,returntotheparent
completedWork=returnFiber;
//Updatethenextthingwe'reworkingonincasesomethingthrows.
workInProgress=completedWork;
}while(completedWork!==null);
//We'vereachedtheroot.
if(workInProgressRootExitStatus===RootIncomplete){
workInProgressRootExitStatus=RootCompleted;
}
他的作用便是逐層收集fiber樹上已經(jīng)被打上的副作用標(biāo)簽flags,一直收集到root上面以便于在commit階段進(jìn)行dom的增刪改。
scheduler流程
在這里應(yīng)該有很多人不明白,協(xié)調(diào)和調(diào)度是什么意思,通俗來(lái)講:
協(xié)調(diào)就是協(xié)同合作調(diào)度就是執(zhí)行命令
所以在React中協(xié)調(diào)就是一個(gè)js線程中,需要安排很多模塊去完成整個(gè)流程,例如:同步異步lane的處理,reconcileChildren處理fiber節(jié)點(diǎn)等,保證整個(gè)流程有條不紊的執(zhí)行。調(diào)度表現(xiàn)為讓空閑的js線程(幀層面)去執(zhí)行其他任務(wù),這個(gè)過(guò)程稱之為調(diào)度,那么它到底是怎么去做的呢?
我們回到處理異步任務(wù)那里,我們會(huì)發(fā)現(xiàn)performConcurrentWorkOnRoot這個(gè)函數(shù)外面包裹了一層scheduleCallback:
newCallbackNode=scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null,root),
)
exportfunctionscheduleCallback(
reactPriorityLevel:ReactPriorityLevel,callback:SchedulerCallback,options:SchedulerCallbackOptions|void|null,
constpriorityLevel=reactPriorityToSchedulerPriority(reactPriorityLevel);
returnScheduler
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 產(chǎn)品外協(xié)加工合同范例
- 醫(yī)院信息系統(tǒng)安全防御與響應(yīng)
- 浙江省錢塘聯(lián)盟2024-2025學(xué)年高一下學(xué)期4月期中聯(lián)考試題 語(yǔ)文 PDF版含答案
- 從商業(yè)角度看如何利用區(qū)塊鏈技術(shù)推動(dòng)企業(yè)變革
- 供熱材料合同范例
- 區(qū)塊鏈技術(shù)助力醫(yī)療供應(yīng)鏈優(yōu)化
- 兌快遞合同范例
- Ⅰ型腎小管性酸中毒的臨床護(hù)理
- 公司文秘個(gè)人工作總結(jié)報(bào)告3篇
- 個(gè)人跟公司合作合同范例
- 手術(shù)中大出血搶救流程
- 初中語(yǔ)文第23課《“蛟龍”探?!氛n件-2024-2025學(xué)年統(tǒng)編版語(yǔ)文七年級(jí)下冊(cè)
- 2025重慶武工工業(yè)技術(shù)研究院有限公司招聘15人筆試參考題庫(kù)附帶答案詳解
- 電工技術(shù)基礎(chǔ) 教案全套 歐小東 第1-10章 直流電路的基礎(chǔ)知識(shí)-過(guò)渡過(guò)程
- 汽車銷售禮儀與溝通技巧考核試卷
- 光伏電站面試題庫(kù)及答案
- 陶藝店管理制度
- 遺體轉(zhuǎn)運(yùn)協(xié)議書范本
- 挖礦委托協(xié)議書范本
- 2025年標(biāo)準(zhǔn)租房合同范本
- 三元空間下個(gè)人化IP綜藝《燦爛的花園》敘事與價(jià)值研究
評(píng)論
0/150
提交評(píng)論