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
functionrequestCurrentTime(){if(isRendering){// We're already rendering. Return the most recently read time.returncurrentSchedulerTime;}// Check if there's pending work.findHighestPriorityRoot();if(nextFlushedExpirationTime===NoWork||nextFlushedExpirationTime===Never){// If there's no pending work, or if the pending work is offscreen, we can// read the current time without risk of tearing.recomputeCurrentRendererTime();currentSchedulerTime=currentRendererTime;returncurrentSchedulerTime;}// There's already pending work. We might be in the middle of a browser// event. If we were to read the current time, it could cause multiple updates// within the same event to receive different expiration times, leading to// tearing. Return the last read time. During the next idle callback, the// time will be updated.returncurrentSchedulerTime;}
functionfindHighestPriorityRoot(){lethighestPriorityWork=NoWork;lethighestPriorityRoot=null;if(lastScheduledRoot!==null){letpreviousScheduledRoot=lastScheduledRoot;letroot=firstScheduledRoot;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.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.nextScheduledRoot=null;firstScheduledRoot=lastScheduledRoot=null;break;}elseif(root===firstScheduledRoot){// This is the first root in the list.constnext=root.nextScheduledRoot;firstScheduledRoot=next;lastScheduledRoot.nextScheduledRoot=next;root.nextScheduledRoot=null;}elseif(root===lastScheduledRoot){// This is the last root in the list.lastScheduledRoot=previousScheduledRoot;lastScheduledRoot.nextScheduledRoot=firstScheduledRoot;root.nextScheduledRoot=null;break;}else{previousScheduledRoot.nextScheduledRoot=root.nextScheduledRoot;root.nextScheduledRoot=null;}root=previousScheduledRoot.nextScheduledRoot;}else{if(remainingExpirationTime>highestPriorityWork){// Update the priority, if it's higherhighestPriorityWork=remainingExpirationTime;highestPriorityRoot=root;}if(root===lastScheduledRoot){break;}if(highestPriorityWork===Sync){// Sync is highest priority by definition so// we can stop searching.break;}previousScheduledRoot=root;root=root.nextScheduledRoot;}}}nextFlushedRoot=highestPriorityRoot;nextFlushedExpirationTime=highestPriorityWork;}
if(nextFlushedExpirationTime===NoWork||nextFlushedExpirationTime===Never){// If there's no pending work, or if the pending work is offscreen, we can// read the current time without risk of tearing.recomputeCurrentRendererTime();currentSchedulerTime=currentRendererTime;returncurrentSchedulerTime;}
functioncomputeExpirationForFiber(currentTime: ExpirationTime,fiber: Fiber){letexpirationTime;// ....// No explicit expiration context was set, and we're not currently// performing work. Calculate a new expiration time.if(fiber.mode&ConcurrentMode){if(isBatchingInteractiveUpdates){// This is an interactive update// 交互引起的更新expirationTime=computeInteractiveExpiration(currentTime);}else{// This is an async update// 普通异步更新expirationTime=computeAsyncExpiration(currentTime);}// ...}// ...returnexpirationTime;}
constUNIT_SIZE=10;// Max 31 bit integer. The max integer size in V8 for 32-bit systems.// Math.pow(2, 30) - 1// 0b111111111111111111111111111111// export default 1073741823;(MAGIC_NUMBER_OFFSET)constMAGIC_NUMBER_OFFSET=MAX_SIGNED_31_BIT_INT-1;functioncomputeExpirationBucket(currentTime,expirationInMs,bucketSizeMs): ExpirationTime{return(MAGIC_NUMBER_OFFSET-ceiling(MAGIC_NUMBER_OFFSET-currentTime+expirationInMs/UNIT_SIZE,bucketSizeMs/UNIT_SIZE));}
functioncomputeExpirationForFiber(currentTime: ExpirationTime,fiber: Fiber){letexpirationTime;// 如果context有更新任务需要执行if(expirationContext!==NoWork){// An explicit expiration context was set;// expirationTime设置为context上的到期时间expirationTime=expirationContext;}elseif(isWorking){// 如果处于renderRoot渲染阶段或者commitRoot提交阶段if(isCommitting){// 如果处于commitRoot// Updates that occur during the commit phase should have sync priority// by default.// expirationTime设置为同步SyncexpirationTime=Sync;}else{// 处于renderRoot// Updates during the render phase should expire at the same time as// the work that is being rendered.// expirationTime设置为当前的到期时间nextRenderExpirationTimeexpirationTime=nextRenderExpirationTime;}}else{// No explicit expiration context was set, and we're not currently// performing work. Calculate a new expiration time.if(fiber.mode&ConcurrentMode){if(isBatchingInteractiveUpdates){// 如果正在批处理交互式更新// This is an interactive update// 交互引起的更新expirationTime=computeInteractiveExpiration(currentTime);}else{// This is an async update// 普通异步更新expirationTime=computeAsyncExpiration(currentTime);}// If we're in the middle of rendering a tree, do not update at the same// expiration time that is already rendering.if(nextRoot!==null&&expirationTime===nextRenderExpirationTime){// 如果有下一root树需要更新,并且到期时间与该树到期时间相等expirationTime-=1;// expirationTime减一,表示让下一个root先更新}}else{// This is a sync updateexpirationTime=Sync;}}if(isBatchingInteractiveUpdates){// 如果正在批处理交互式更新// This is an interactive update. Keep track of the lowest pending// interactive expiration time. This allows us to synchronously flush// all interactive updates when needed.// 如果最低优先级的交互式更新优先级大于到期时间expirationTime或者没有交互式更新任务if(lowestPriorityPendingInteractiveExpirationTime===NoWork||expirationTime<lowestPriorityPendingInteractiveExpirationTime){// 将最低优先级的交互式更新任务到期时间设置为到期时间expirationTimelowestPriorityPendingInteractiveExpirationTime=expirationTime;}}returnexpirationTime;}
The text was updated successfully, but these errors were encountered:
expirationTime
ReactDOM.render
过程中的updateContainer
函数里面有个计算到期时间的函数(computeExpirationForFiber
):这节就来分析一下
expirationTime
是如何计算的。在调用
computeExpirationForFiber
前,通过requestCurrentTime
计算出了currentTime
,先来看下requestCurrentTime
的定义:requestCurrentTime
中有如下两行带代码:先获取到当前时间赋值给
currentRendererTime
,然后currentRendererTime
赋值给currentSchedulerTime
。在上述
requestCurrentTime
函数中,首先看第一个判断:isRendering
会在performWorkOnRoot
的开始设置为true
,在结束设置为false
,都是同步的。在一个事件回调函数中调用多次
setState
的时候,isRendering
总是false
,如果是在生命周期钩子函数componentDidMount
中调用setState
的时候,isRendering
为true
,因为该钩子触发的时机就是在performWorkOnRoot
中。再来看
findHighestPriorityRoot()
:findHighestPriorityRoot
会找到root
双向链表(React.render
会创建一个root
并添加到这个双向链表中)中有任务需要执行并且到期时间最大即优先级最高的任务,然后将这个需要更新的root
以及最大到期时间赋值给nextFlushedRoot
以及nextFlushedExpirationTime
。当没有任务的时候nextFlushedExpirationTime
为NoWork
。接着来看第二个判断:
如果没有任务需要执行,那么重新计算当前时间,并返回,在事件处理函数中第一个 setState 会重新计算当前时间,但是第二个 setState 的时候,由于已经有更新任务在队列中了,所以这里直接跳过判断,最后返回上一次 setState 时的记录的当前时间。
看完
currentTime
,我们来看这节的重点:expirationTime
为什么需要
ExpirationTime
?React16
带来的最振奋人心的改动就是Fiber
架构,改变了之前react
的组件渲染机制,新的架构使原来同步渲染的组件现在可以异步化,可中途中断渲染,执行更高优先级的任务。释放浏览器主线程。所以每一个任务都会有一个优先级,不然岂不是会乱套了.....
ExpirationTime
就是优先级,它是一个过期时间。在计算
ExpirationTime
之前调用了requestCurrentTime
得到了一个currentTime
。这个函数里面牵扯了一些复杂的关于后面知识的逻辑,我们先不深究,大家就先理解为一个当前时间类似的概念。这里先来看一下计算
expirationTime
的方法computeExpirationForFiber
。computeExpirationForFiber
在异步更新中,这里我们看到有两种计算更新的方式。
computeInteractiveExpiration
和computeAsyncExpiration
。分别来看下对应方法。computeInteractiveExpiration
和computeAsyncExpiration
computeInteractiveExpiration
computeAsyncExpiration
查看上面两种方法,我们发现其实他们调用的是同一个方法:
computeExpirationBucket
,只是传入的参数不一样,而且传入的是常量。computeInteractiveExpiration
传入的是 150、100,computeAsyncExpiration
传入的是 5000、250。说明前者的优先级更高。那么我把前者称为高优先级更新,后者称为低优先级更新。下面来看
computeExpirationBucket
方法的具体内容:这里用到了
ceiling
函数:方法的作用是向上取整,
|0
表示向下取整,再加 1,即向上取整。间隔在precision
内的两个num
最终得到的相同的值。 如果precision
为 25,则num
为 50 和 70 转换后的到期时间都是 75。这样相差25ms
内的当前时间经过计算被统一为同样的过期时间,让非常相近的两次更新得到相同的expirationTime
,然后在一次更新中完成,相当于一个自动的batchedUpdates
,减少渲染次数。分析完这里,我们回到
computeExpirationForFiber
:The text was updated successfully, but these errors were encountered: