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。
文章若有不妥,歡迎指正~