You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// requestWork is called by the scheduler whenever a root receives an update.// It's up to the renderer to call renderRoot at some point in the future.functionrequestWork(root: FiberRoot,expirationTime: ExpirationTime){// 把当前 root设置为最高优先级addRootToSchedule(root,expirationTime);if(isRendering){// 在render过程当中, 此时直接return// Prevent reentrancy. Remaining work will be scheduled at the end of// the currently rendering batch.return;}// 批量处理相关// 调用 setState 时在 enqueueUpdates 前 batchedUpdates 会把 isBatchingUpdates 设置成 trueif(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,false);}return;}// TODO: Get rid of Sync and use current time?if(expirationTime===Sync){// 同步的调用 js 代码performSyncWork();}else{// 异步调度 独立的 react 模块包,利用浏览器有空闲的时候进行执行,设置 deadline 在此之前执行scheduleCallbackWithExpirationTime(root,expirationTime);}}
/** * 将 root 加入到调度队列 * * @param {FiberRoot} root * @param {ExpirationTime} expirationTime */functionaddRootToSchedule(root: FiberRoot,expirationTime: ExpirationTime){// Add the root to the schedule.// Check if this root is already part of the schedule.// root.nextScheduledRoot 用来判断是否有异步任务正在调度, 为 null 时会增加 nextScheduledRoot// 这个 root 还没有进入过调度if(root.nextScheduledRoot===null){// This root is not already scheduled. Add it.root.expirationTime=expirationTime;if(lastScheduledRoot===null){// lastScheduledRoot firstScheduledRoot 是单向链表结构,表示多个 root 更新// 这里只有一个 root 只会在这里执行firstScheduledRoot=lastScheduledRoot=root;root.nextScheduledRoot=root;}else{// 有个多个root 时进行单向链表的插入操作lastScheduledRoot.nextScheduledRoot=root;lastScheduledRoot=root;lastScheduledRoot.nextScheduledRoot=firstScheduledRoot;}}else{// This root is already scheduled, but its priority may have increased.// 传入的 root 已经进入过调度, 把 root 的优先级设置最高constremainingExpirationTime=root.expirationTime;// 如果 root 的 expirationTime 是同步或者优先级低,增加为计算出的最高优先级if(expirationTime>remainingExpirationTime){// Update the priority.// 把当前 root 的优先级设置为当前优先级最高的root.expirationTime=expirationTime;}}}
/** * performWork 只发现了两种调用方式,performSyncWork和performAsyncWork * performSyncWork的minExpirationTime就是Sync,isYieldy是false * performAsyncWork的minExpirationTime是NoWork,isYieldy是true * @param {ExpirationTime} minExpirationTime * @param {boolean} isYieldy 任务是否可以中断 */functionperformWork(minExpirationTime: ExpirationTime,isYieldy: boolean){// Keep working on roots until there's no more work, or until there's a higher// priority event.// 查找优先级最高的rootfindHighestPriorityRoot();if(isYieldy){recomputeCurrentRendererTime();currentSchedulerTime=currentRendererTime;if(enableUserTimingAPI){constdidExpire=nextFlushedExpirationTime>currentRendererTime;consttimeout=expirationTimeToMs(nextFlushedExpirationTime);stopRequestCallbackTimer(didExpire,timeout);}// nextFlushedRoot 下一个将要执行的 FiberRoot// nextFlushedExpirationTime !== NoWork 下一个FiberRoot中还有未执行的任务while(nextFlushedRoot!==null&&nextFlushedExpirationTime!==NoWork&&minExpirationTime<=nextFlushedExpirationTime&&!(didYield&¤tRendererTime>nextFlushedExpirationTime)){performWorkOnRoot(nextFlushedRoot,nextFlushedExpirationTime,currentRendererTime>nextFlushedExpirationTime);findHighestPriorityRoot();recomputeCurrentRendererTime();currentSchedulerTime=currentRendererTime;}}else{while(nextFlushedRoot!==null&&nextFlushedExpirationTime!==NoWork&&minExpirationTime<=nextFlushedExpirationTime){performWorkOnRoot(nextFlushedRoot,nextFlushedExpirationTime,false);findHighestPriorityRoot();}}// 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(isYieldy){callbackExpirationTime=NoWork;callbackID=null;}// If there's work left over, schedule a new callback.if(nextFlushedExpirationTime!==NoWork){scheduleCallbackWithExpirationTime(((nextFlushedRoot: any): FiberRoot),nextFlushedExpirationTime);}// Clean-up.finishRendering();}
findHighestPriorityRoot在前面就遇到过,主要工作是查找优先级最高的Root。
/** * 查找拥有最高渲染优先级的Root,通过设置nextFlushedRoot和nextFlushedExpirationTime进行控制 * */functionfindHighestPriorityRoot(){// 最高优先级的WorklethighestPriorityWork=NoWork;// 最高优先级的RootlethighestPriorityRoot=null;// 判断上一个被调度的Root是否为空,如果是,就证明是首次渲染,跳出判断。if(lastScheduledRoot!==null){// 上一个被渲染的root节点letpreviousScheduledRoot=lastScheduledRoot;letroot=firstScheduledRoot;// 从调度队列中的起始节点开始递归的判断调度队列中的root节点while(root!==null){constremainingExpirationTime=root.expirationTime;if(remainingExpirationTime===NoWork){// This root no longer has work. Remove it from the scheduler.// TODO: This check is redudant, but Flow is confused by the branch// below where we set lastScheduledRoot to null, even though we break// from the loop right after.// 如果root的expirationTime === NoWork, 则说明root没有任务需要完成,则将root从调度队列中移除// 调度队列是一个环形链表invariant(previousScheduledRoot!==null&&lastScheduledRoot!==null,"Should have a previous and last root. This error is likely "+"caused by a bug in React. Please file an issue.");if(root===root.nextScheduledRoot){// This is the only root in the list.// 只有一个Root的情况, 则直接清空整个调度链表root.nextScheduledRoot=null;firstScheduledRoot=lastScheduledRoot=null;break;}elseif(root===firstScheduledRoot){// This is the first root in the list.// 如果root是起始节点,起始节点后移constnext=root.nextScheduledRoot;firstScheduledRoot=next;lastScheduledRoot.nextScheduledRoot=next;root.nextScheduledRoot=null;}elseif(root===lastScheduledRoot){// This is the last root in the list.// 如果root是结束节点,结束节点前移lastScheduledRoot=previousScheduledRoot;lastScheduledRoot.nextScheduledRoot=firstScheduledRoot;root.nextScheduledRoot=null;break;}else{// 如果root是中间节点,则删除previousScheduledRoot.nextScheduledRoot=root.nextScheduledRoot;root.nextScheduledRoot=null;}// 继续递归root=previousScheduledRoot.nextScheduledRoot;}else{// 如果root节点有更新任务if(remainingExpirationTime>highestPriorityWork){// Update the priority, if it's higher// 找到优先级最高的expirationTimehighestPriorityWork=remainingExpirationTime;highestPriorityRoot=root;}// 如果root是链表尾节点,跳出while循环if(root===lastScheduledRoot){break;}// 碰到同步任务,跳出while循环if(highestPriorityWork===Sync){// Sync is highest priority by definition so// we can stop searching.break;}previousScheduledRoot=root;// 继续递归root=root.nextScheduledRoot;}}}nextFlushedRoot=highestPriorityRoot;nextFlushedExpirationTime=highestPriorityWork;}
/** * * * @param {FiberRoot} root 进入更新流程的FiberRoot * @param {ExpirationTime} expirationTime 更新流程中expirationTime * @param {boolean} isYieldy 是否可以被中断 */functionperformWorkOnRoot(root: FiberRoot,expirationTime: ExpirationTime,isYieldy: boolean){invariant(!isRendering,"performWorkOnRoot was called recursively. This error is likely caused "+"by a bug in React. Please file an issue.");// 标记当前进入render流程isRendering=true;// Check if this is async work or sync/expired work.// 判断当前是同步更新还是异步更新if(!isYieldy){// 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).// root在完成更新工作之后的fiber对象letfinishedWork=root.finishedWork;// 如果FiberRoot已经完成了更新if(finishedWork!==null){// This root is already complete. We can commit it.// 直接完成,进入commit流程completeRoot(root,finishedWork,expirationTime);}else{// 还原FiberRootroot.finishedWork=null;// If this root previously suspended, clear its existing timeout, since// we're about to try rendering again.consttimeoutHandle=root.timeoutHandle;if(timeoutHandle!==noTimeout){root.timeoutHandle=noTimeout;// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check abovecancelTimeout(timeoutHandle);}// 进入渲染流程renderRoot(root,isYieldy);finishedWork=root.finishedWork;if(finishedWork!==null){// We've completed the root. Commit it.completeRoot(root,finishedWork,expirationTime);}}}else{// Flush async work.letfinishedWork=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.consttimeoutHandle=root.timeoutHandle;if(timeoutHandle!==noTimeout){root.timeoutHandle=noTimeout;// $FlowFixMe Complains noTimeout is not a TimeoutID, despite the check abovecancelTimeout(timeoutHandle);}renderRoot(root,isYieldy);finishedWork=root.finishedWork;if(finishedWork!==null){// We've completed the root. Check the if we should yield one more time// before committing.if(!shouldYieldToRenderer()){// 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;}}}}isRendering=false;}
在开始之前,我们先回顾一下
React
在requestWork
之前的工作流程。React
会先根据window.performance.now()
方法获取一个从地址栏输入地址回车后到执行这个方法中间间隔的毫秒数,然后通过一个常量减去这个毫秒数/10 计算出一个expirationTime
,然后fiber
节点会根据实际情况判断是否记录此expirationTime
作为自己最终的expirationTime
。然后进入
updateContainer
流程,updateContainer
流程中会创建一个update
对象,这个对象会记录这次update
的expirationTime
,effect
,tag
,next
等信息,并最终组成一个叫做updateQueue
的链表存储到fiber
节点中。接下来进入
scheduleWork
流程,react
递归的更新FiberRoot
上的与expirationTime
相关的属性,找到下一个工作的expirationTime
。接下来就进入
requestWork
方法。我们先看
requestWork
中调用的第一个方法addRootToSchedule
,将root
节点添加到调度队列中。还记得前面我们说过,首次渲染更新的时候
expirationTime
就是Sync
,因此在将FiberRoot
添加到调度队列中后,会进入到performSyncWork()
方法。performWork
方法有两种调用形式,一种performSyncWork
,一种performAsyncWork
。findHighestPriorityRoot
在前面就遇到过,主要工作是查找优先级最高的Root
。在找到优先级最高的
Root
之后,由于我们这里是通过performSyncWork
方法调用进入的performWork
流程,因此isYieldy
为false
,会递归的调用performWorkOnRoot()
方法,然后通过findHighestPriorityRoot()
查找下一个Root
继续递归。performWorkOnRoot
代码如下:在首次渲染时我们进入的是
performSyncWork
的同步调用,因此isYieldy
为false
,代码会执行到renderRoot
方法,进入渲染Root
的流程。The text was updated successfully, but these errors were encountered: