網(wǎng)站開發(fā) 搜索北京網(wǎng)絡(luò)網(wǎng)站推廣
expirationTime 的計算方式
- 先看expirationTime相關(guān)的源代碼,這里是異步的計算方式,它會有一個過期時間
- 異步任務(wù)優(yōu)先級比較低,可以被打斷,防止一直被打斷導(dǎo)致不能執(zhí)行,所以React給它設(shè)置了 expirationTime 過期時間
- 也就是在這個時間之前,都可以打斷,但是如果某個時間點發(fā)現(xiàn)任務(wù)已經(jīng)過期了,還沒有被執(zhí)行,則強制執(zhí)行該任務(wù)
- 在
ReactDOM.render
當(dāng)中,它計算 expirationTime 的地方- 在 ReactFiberReconciler.js 中的
updateContainer
函數(shù)中,通過computeExpirationForFiber
方法來計算一個過期時間const current = container.current; // 參數(shù)2 const currentTime = requestCurrentTime(); // 參數(shù)1 我們可以近似理解為: 當(dāng)前時間到j(luò)s加載完成的時間的時間差值即可 const expirationTime = computeExpirationForFiber(currentTime, current);
- 而
requestCurrentTime
這個函數(shù),來自于 ReactFiberScheduler.js 中function requestCurrentTime() {// 這里,我把官方注釋移除// 這里表示 已經(jīng)進入到 渲染階段 了,在 ReactDOM.render 中這里不會匹配,會跳過// 在一次render中,如果我有一個新的任務(wù)進來了,要計算 expirationTime 發(fā)現(xiàn)現(xiàn)在處于渲染階段,這時直接返回上次 render 開始的時間,再去計算 expirationTime// 好處是 前后兩次計算出來的 expirationTime 是一樣的,讓這個任務(wù)提前進行調(diào)度if (isRendering) {// We're already rendering. Return the most recently read time.return currentSchedulerTime;}// 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; // 兩個常量劃等號return currentSchedulerTime;}// 這里,我把官方注釋移除return currentSchedulerTime; }
findHighestPriorityRoot
方法涉及到從調(diào)度隊列中找到權(quán)限最高的 Root- 這個源碼比較多,不做擴展
recomputeCurrentRendererTime
每一次做計算都是從當(dāng)前到j(luò)s加載完成后的時間間隔,再經(jīng)過一些計算得到的值,function recomputeCurrentRendererTime() {const currentTimeMs = now() - originalStartTimeMs; // 當(dāng)前時間 - react buddle加載完成之后初始的時間,也就是從js加載完成到現(xiàn)在的時間間隔currentRendererTime = msToExpirationTime(currentTimeMs); // 計算出 currentRendererTime }// ReactFiberExpirationTime.js 這個函數(shù)得到一個時間戳 export function msToExpirationTime(ms: number): ExpirationTime {// Always add an offset so that we don't clash with the magic number for NoWork.return ((ms / UNIT_SIZE) | 0) + MAGIC_NUMBER_OFFSET; // UNIT_SIZE 是固定的 10, | 0 是取整的意思, MAGIC_NUMBER_OFFSET 是固定的 2 }
- 關(guān)于
expirationTime
的計算函數(shù)computeExpirationForFiber
有一個計算公式 - 在這個計算時間中,不需要考慮調(diào)度,只考慮計算公式,在 ReactFiberExpirationTime.js 中
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,)); }// 低權(quán)限計算 export function computeAsyncExpiration(currentTime: ExpirationTime, ): ExpirationTime {return computeExpirationBucket(currentTime,LOW_PRIORITY_EXPIRATION, // 5000LOW_PRIORITY_BATCH_SIZE, // 250); }// 高權(quán)限計算 export function computeInteractiveExpiration(currentTime: ExpirationTime) {return computeExpirationBucket(currentTime,HIGH_PRIORITY_EXPIRATION, // 500/150 前 DEV, 后 PRODHIGH_PRIORITY_BATCH_SIZE, // 100); }
- 上面兩個 export 方法,都是調(diào)用
computeExpirationBucket
方法來計算的 - 兩個方法的區(qū)別在于 后面第2和第3個參數(shù)是不一樣的
- 最終得到的公式是:
((((currentTime - 2 + 5000 / 10) / 25) | 0 ) + 1) * 25
- 其中 25 = 250 / 10,上述公式中的25,也可能是 10 (100 / 10)
- 最終計算出來的
expirationTime
是以bucketSize / UNIT_SIZE
這個單元向上疊加的 - 兩個 不同的
expirationTime
的差距是 單元值的 倍數(shù) - 對于
LOW_PRIORITY_BATCH_SIZE
是 以 25 為單元向上加的, 若前后差距在25以內(nèi),計算出來的差距都是一樣的 - 對于
HIGH_PRIORITY_BATCH_SIZE
是 以 10 為單元向上加的,同上 - React 這么設(shè)定的原因: 在計算
expirationTime
時 - 在一個操作內(nèi)多次調(diào)用
setState
, 即便前后調(diào)用差距很小,但從毫秒級別看,還是有差距的 - 如果沒有提供任何一個調(diào)整空間,即便上個
setState
和 下一個setState
之間差距特別小,算出來的expirationTime
結(jié)果不一樣 - 這就意味著,兩次的任務(wù)優(yōu)先級不一樣, 會導(dǎo)致 React 整體更新執(zhí)行多次,而導(dǎo)致整個應(yīng)用的性能下降,這就是 設(shè)置 單元值的 用處
- 在一個差距很小的時間間隔內(nèi),算出來的
expirationTime
結(jié)果一樣,則它們優(yōu)先級也是一樣的,而不需要進行區(qū)分 - 這個非常重要
- 最終計算出來的
| 0
表示 去余取整- 其中 (currentTime - 2 + 5000 / 10) 是一個不會變化的值,設(shè)為 x
- 后面就是 (((x / 25) | 0) + 1) * 25
- 這個公式的意義在于,新老值之間的差距在25以內(nèi),則結(jié)果相等
- 其中 25 = 250 / 10,上述公式中的25,也可能是 10 (100 / 10)
- 上面兩個 export 方法,都是調(diào)用
- 在 ReactFiberReconciler.js 中的
- 至于
expirationTime
的作用還要結(jié)合后期更新的流程來看 expirationTime
是一個和業(yè)務(wù)無關(guān)的比較純粹的計算過程問題,沒有任何副作用