共计 3806 个字符,预计需要花费 10 分钟才能阅读完成。
一、ExpirationTime 的作用
在React
中,为防止某个 update
因为优先级的原因一直被打断而未能执行。React
会设置一个 ExpirationTime
,当时间到了ExpirationTime
的时候,如果某个 update
还未执行的话,React
将会强制执行该 update
,这就是ExpirationTime
的作用。
二、位置
在 React 源码解析之 ReactDOM.render()中,已经讲解了updateContainer()
:
export function updateContainer(
element: ReactNodeList,
container: OpaqueRoot,
parentComponent: ?React$Component<any, any>,
callback: ?Function,
): ExpirationTime {
...
// 计算过期时间,这是 React 优先级更新非常重要的点
const expirationTime = computeExpirationForFiber(
currentTime,
current,
suspenseConfig,
);
...
}
computeExpirationForFiber
:
// 为 fiber 对象计算 expirationTime
export function computeExpirationForFiber(
currentTime: ExpirationTime,
fiber: Fiber,
suspenseConfig: null | SuspenseConfig,
): ExpirationTime {
...
// Compute an expiration time based on the Scheduler priority.
switch (priorityLevel) {
case ImmediatePriority:
expirationTime = Sync;
break;
case UserBlockingPriority:
// TODO: Rename this to computeUserBlockingExpiration
// 一个是计算交互事件(如点击)的过期时间
expirationTime = computeInteractiveExpiration(currentTime);
break;
case NormalPriority:
case LowPriority: // TODO: Handle LowPriority
// TODO: Rename this to... something better.
// 一个是计算异步更新的过期时间
expirationTime = computeAsyncExpiration(currentTime);
break;
case IdlePriority:
expirationTime = Never;
break;
default:
invariant(false, 'Expected a valid priority level');
}
...
}
我们可以看到有两个计算 expirationTime
的方法,分别为 computeInteractiveExpiration()
和computeAsyncExpiration()
先看下computeAsyncExpiration()
三、computeAsyncExpiration()
作用:
返回低优先级(普通异步更新)的expirationTime
(过期时间)
源码:
// 低权限的过期时间
export const LOW_PRIORITY_EXPIRATION = 5000;
export const LOW_PRIORITY_BATCH_SIZE = 250;
// 普通的异步的 expirationTime
export function computeAsyncExpiration(currentTime: ExpirationTime,): ExpirationTime {
return computeExpirationBucket(
currentTime,
//5000
LOW_PRIORITY_EXPIRATION,
//250
LOW_PRIORITY_BATCH_SIZE,
);
}
解析:currentTime
先按下不表,LOW_PRIORITY_EXPIRATION
即 5000
,LOW_PRIORITY_BATCH_SIZE
即250
,注意它的名字LOW_PRIORITY_BATCH_SIZE
,下面会提到
四、computeExpirationBucket()
作用:
计算过期时间
源码:
//1073741823
export const Sync = MAX_SIGNED_31_BIT_INT;
//1073741822
export const Batched = Sync - 1;
const UNIT_SIZE = 10;
//1073741821
const MAGIC_NUMBER_OFFSET = Batched - 1;
function ceiling(num: number, precision: number): number {return (((num / precision) | 0) + 1) * precision;
}
// 计算过期时间
function computeExpirationBucket(
currentTime,
expirationInMs,
bucketSizeMs,
): ExpirationTime {
return (
//1073741821
MAGIC_NUMBER_OFFSET -
ceiling(// 1073741821-currentTime+(high 150 或者 low 5000 /10) ,
MAGIC_NUMBER_OFFSET - currentTime + expirationInMs / UNIT_SIZE,
//(high 100 或者 low 250 /10)
bucketSizeMs / UNIT_SIZE,
)
);
}
解析:
(1)MAX_SIGNED_31_BIT_INT
// Max 31 bit integer. The max integer size in V8 for 32-bit systems.
// Math.pow(2, 30) - 1
// 0b111111111111111111111111111111
// 整型最大数值,是 V8 中针对 32 位系统所设置的最大值
export default 1073741823;
(2)| 0
的意思是取整
console.log(16/3 |0) //5
(3)根据 computeExpirationBucket()
里面的公式,计算下异步更新的过期时间:
//low 情况
1073741821-ceiling(1073741821-currentTime+500,25)
1073741821-((((1073741821-currentTime+500) / 25) | 0) + 1) * 25
1073741821-((1073741821/25-currentTime/25+20 | 0) + 1) * 25
1073741821-((1073741821/25-currentTime/25+20*25/25 | 0) + 1) * 25
1073741821-((1073741821-currentTime+500)/25 | 0)*25 - 25
1073741796-((1073741821-currentTime+500)/25 | 0)*25
1073741796-((1073742321-currentTime)/25 | 0)*25
//====== 我们直接取最后四位来探索规律 ===================
1796-((2321-currentTime)/25 | 0)*25
// 假设 currentTime 是 2000
1796-(2321- 2000 /25 | 0)*25 //1796-300
//currentTime 是 2010
1796-(311/25 | 0)*25 //1796-300
//currentTime 是 2024
1796-(311/25 | 0)*25 //1796-275
//currentTime 是 2025
1796-(311/25 | 0)*25 //1796-275
可以看到,低优先的过期时间 间隔 是25ms
同理,高优先级的过期时间 间隔 是10ms
//high 情况
1073741821-ceiling(1073741821-currentTime+15,10)
** 也就是说,React
低优先级 update
的expirationTime
间隔是 25ms
,React
让两个相近(25ms
内)的 update
得到相同的 expirationTime
,目的就是让这两个update
自动合并成一个 Update
,从而达到批量更新的目的,就像LOW_PRIORITY_BATCH_SIZE
的名字一样,自动合并批量更新。**
想象一下,开发者不停地使用 setState()
更新 ReactApp
,如果不把相近的update
合并的话,会严重影响性能,就像提到的 doubleBuffer
一样,React
为提高性能,考虑得非常全面!
Github:
https://github.com/AttackXiaoJinJin/reactExplain/blob/master/react16.8.6/packages/react-reconciler/src/ReactFiberExpirationTime.js
(完)