React源码解析系列文章欢迎阅读:
React16源码解析(一)- 图解Fiber架构
React16源码解析(二)-建立更新
React16源码解析(三)-ExpirationTime
React16源码解析(四)-Scheduler
React16源码解析(五)-更新流程渲染阶段1
React16源码解析(六)-更新流程渲染阶段2
React16源码解析(七)-更新流程渲染阶段3
React16源码解析(八)-更新流程提交阶段
正在更新中...react
在个人上篇文章中,ReactDOM.render过程当中的updateContainer函数里面有个计算到期时间的函数:segmentfault
export function updateContainer( element: ReactNodeList, container: OpaqueRoot, parentComponent: ?React$Component<any, any>, callback: ?Function, ): ExpirationTime { const current = container.current; const currentTime = requestCurrentTime(); // 这里传入了currentTime和当前的Fiber对象调用了这个计算expirationTime的函数 const expirationTime = computeExpirationForFiber(currentTime, current); return updateContainerAtExpirationTime( element, container, parentComponent, expirationTime, callback, ); }
在上篇文章讨论的setState和forceUpdate中一样的也要计算expirationTime,这篇文章就来分析expirationTime是如何计算的。浏览器
React16带来的最振奋人心的改动就是Fiber架构,改变了以前react的组件渲染机制,新的架构使原来同步渲染的组件如今能够异步化,可中途中断渲染,执行更高优先级的任务。释放浏览器主线程。架构
因此每个任务都会有一个优先级,否则岂不是会乱套了..... ExpirationTime就是优先级,它是一个过时时间。异步
在计算ExpirationTime以前调用了requestCurrentTime获得了一个currentTime。这个函数里面牵扯了一些复杂的关于后面知识的逻辑,咱们先不深究,你们就先理解为一个当前时间相似的概念。函数
function computeExpirationForFiber(currentTime: ExpirationTime, fiber: Fiber) { let expirationTime; // ...... if (fiber.mode & ConcurrentMode) { if (isBatchingInteractiveUpdates) { // 交互引发的更新 expirationTime = computeInteractiveExpiration(currentTime); } else { // 普通异步更新 expirationTime = computeAsyncExpiration(currentTime); } } // ...... } // ...... return expirationTime; }
在异步更新中,这里咱们看到有两种计算更新的方式。computeInteractiveExpiration和computeAsyncExpiration线程
export const HIGH_PRIORITY_EXPIRATION = __DEV__ ? 500 : 150; export const HIGH_PRIORITY_BATCH_SIZE = 100; export function computeInteractiveExpiration(currentTime: ExpirationTime) { return computeExpirationBucket( currentTime, HIGH_PRIORITY_EXPIRATION,//150 HIGH_PRIORITY_BATCH_SIZE,//100 ); }
export const LOW_PRIORITY_EXPIRATION = 5000; export const LOW_PRIORITY_BATCH_SIZE = 250; export function computeAsyncExpiration( currentTime: ExpirationTime, ): ExpirationTime { return computeExpirationBucket( currentTime, LOW_PRIORITY_EXPIRATION,//5000 LOW_PRIORITY_BATCH_SIZE,//250 ); }
查看上面两种方法,咱们发现其实他们调用的是同一个方法:computeExpirationBucket,只是传入的参数不同,并且传入的是常量。computeInteractiveExpiration传入的是150、100,computeAsyncExpiration传入的是5000、250。说明前者的优先级更高。那么我把前者称为高优先级更新,后者称为低优先级更新。code
下面来看computeExpirationBucket方法的具体内容:对象
const UNIT_SIZE = 10; const MAGIC_NUMBER_OFFSET = 2; function ceiling(num: number, precision: number): number { return (((num / precision) | 0) + 1) * precision; } function computeExpirationBucket( currentTime, expirationInMs, bucketSizeMs, ): ExpirationTime { return ( MAGIC_NUMBER_OFFSET + ceiling( currentTime - MAGIC_NUMBER_OFFSET + expirationInMs / UNIT_SIZE, bucketSizeMs / UNIT_SIZE, ) ); }
看完以后,一脸懵。它在搞什么?别急,咱们把公式整理一下:
以低优先级更新为例,最终的公式是:((((currentTime - 2 + 5000 / 10) / 25) | 0) + 1) * 25ci
其中只有只有currentTime是变量。
咱们能够多试几个值看看:
((((101 - 2 + 5000 / 10) / 25) | 0) + 1) * 25 // 600 ((((102 - 2 + 5000 / 10) / 25) | 0) + 1) * 25 // 625 ((((105 - 2 + 5000 / 10) / 25) | 0) + 1) * 25 // 625 ((((122 - 2 + 5000 / 10) / 25) | 0) + 1) * 25 // 625 ((((126 - 2 + 5000 / 10) / 25) | 0) + 1) * 25 // 625 ((((127 - 2 + 5000 / 10) / 25) | 0) + 1) * 25 // 650
简单来讲,最终结果是以25为单位向上增长的,好比说咱们输入102 - 126之间,最终获得的结果都是625,可是到了127获得的结果就是650了,这就是除以25取整的效果。
即,低优先级更新的expirationTime间隔是25ms,抹平了25ms内计算过时时间的偏差,React让两个相近(25ms内)的获得update相同的expirationTime,目的就是让这两个update自动合并成一个Update,从而达到批量更新。
注:这里若是用高优先级更新去尝试多组数据,你会发现expirationTime间隔是10ms。
文章若有不妥,欢迎指正~