網站首頁 編程語言 正文
為什么需要優(yōu)先級
優(yōu)先級機制最終目的是為了實現高優(yōu)先級任務優(yōu)先執(zhí)行,低優(yōu)先級任務延后執(zhí)行。
實現這一目的的本質就是在低優(yōu)先級任務執(zhí)行時,有更高優(yōu)先級任務進來的話,可以打斷低優(yōu)先級任務的執(zhí)行。
同步模式下的react運行時
我們知道在同步模式下,從 setState
到 虛擬DOM遍歷,再到真實DOM更新,整個過程都是同步執(zhí)行且無法被中斷的,這樣可能就會出現一個問題 —— 用戶事件觸發(fā)的更新被阻塞。
什么是用戶事件觸發(fā)的更新被阻塞?如果 React
正在進行更新任務,此時用戶觸發(fā)了交互事件,且在事件回調中執(zhí)行了 setState
,在同步模式下,這個更新任務需要 等待 當前正在更新的任務完成之后,才會被執(zhí)行。假如當前 React
正在進行的更新任務耗時比較久,用戶事件觸發(fā)的更新任務不能及時被執(zhí)行,造成下個更新任務被阻塞,從而形成了卡頓。
這時候,我們就希望能夠及時響應用戶觸發(fā)的事件,優(yōu)先執(zhí)行用戶事件觸發(fā)的更新任務,也就是我們說的異步模式
我們可以比較一下,同步模式下和異步模式(優(yōu)先級機制)下更新任務執(zhí)行的差異
import React from "react"; import "./styles.css"; export default class extends React.Component { constructor() { super(); this.state = { list: new Array(10000).fill(1), }; this.domRef = null; } componentDidMount() { setTimeout(() => { console.log("setTimeout 準備更新", performance.now()); this.setState( { list: new Array(10000).fill(Math.random() * 10000), updateLanes: 16 }, () => { console.log("setTimeout 更新完畢", performance.now()); } ); }, 100); setTimeout(() => { this.domRef.click(); }, 150); } render() { const { list } = this.state; return ( <div ref={(v) => (this.domRef = v)} className="App" onClick={() => { console.log("click 準備更新", performance.now()); this.setState( { list: new Array(10000).fill(2), updateLanes: 1 }, () => { console.log("click 更新完畢", performance.now()); } ); }} > {list.map((i, index) => ( <h2 key={i + +index}>Hello {i}</h2> ))} </div> ); } }
click事件
觸發(fā)的更新,會比 setTimeout
觸發(fā)的更新更優(yōu)先執(zhí)行,做到了及時響應用戶事件,打斷 setTimeout
更新任務(低優(yōu)先級任務)的執(zhí)行。
如何運用優(yōu)先級機制優(yōu)化react運行時
為了解決同步模式渲染下的缺陷,我們希望能夠對 react
做出下面這些優(yōu)化
- 確定不同場景下所觸發(fā)更新的優(yōu)先級,以便我們可以決定優(yōu)先執(zhí)行哪些任務
- 若有更高優(yōu)先級的任務進來,我們需要打斷當前進行的任務,然后執(zhí)行這個高優(yōu)先級任務
- 確保低優(yōu)先級任務不會被一直打斷,在一定時間后能夠被升級為最高優(yōu)先級的任務
確定不同場景下的調度優(yōu)先級
看過 react
源碼的小伙伴可能都會有一個疑惑,為什么源碼里面有那么多優(yōu)先級相關的單詞??怎么區(qū)分他們呢?
其實在 react
中主要分為兩類優(yōu)先級,scheduler
優(yōu)先級和 lane
優(yōu)先級,lane
優(yōu)先級下面又派生出 event
優(yōu)先級
- lane 優(yōu)先級:主要用于任務調度前,對當前正在進行的任務和被調度任務做一個優(yōu)先級校驗,判斷是否需要打斷當前正在進行的任務
- event 優(yōu)先級:本質上也是lane優(yōu)先級,lane優(yōu)先級是通用的,event優(yōu)先級更多是結合瀏覽器原生事件,對lane優(yōu)先級做了分類和映射
- scheduler 優(yōu)先級:主要用在時間分片中任務過期時間的計算
lane優(yōu)先級
可以用賽道的概念去理解lane優(yōu)先級,lane優(yōu)先級有31個,我們可以用31位的二進制值去表示,值的每一位代表一條賽道對應一個lane優(yōu)先級,賽道位置越靠前,優(yōu)先級越高
優(yōu)先級 | 十進制值 | 二進制值 | 賽道位置 |
---|---|---|---|
NoLane | 0 | 0000000000000000000000000000000 | 0 |
SyncLane | 1 | 0000000000000000000000000000001 | 0 |
InputContinuousHydrationLane | 2 | 0000000000000000000000000000010 | 1 |
InputContinuousLane | 4 | 0000000000000000000000000000100 | 2 |
DefaultHydrationLane | 8 | 0000000000000000000000000001000 | 3 |
DefaultLane | 16 | 0000000000000000000000000010000 | 4 |
TransitionHydrationLane | 32 | 0000000000000000000000000100000 | 5 |
TransitionLane1 | 64 | 0000000000000000000000001000000 | 6 |
TransitionLane2 | 128 | 0000000000000000000000010000000 | 7 |
TransitionLane3 | 256 | 0000000000000000000000100000000 | 8 |
TransitionLane4 | 512 | 0000000000000000000001000000000 | 9 |
TransitionLane5 | 1024 | 0000000000000000000010000000000 | 10 |
TransitionLane | 2048 | 0000000000000000000100000000000 | 11 |
TransitionLane7 | 4096 | 0000000000000000001000000000000 | 12 |
TransitionLane8 | 8192 | 0000000000000000010000000000000 | 13 |
TransitionLane9 | 16384 | 0000000000000000100000000000000 | 14 |
TransitionLane10 | 32768 | 0000000000000001000000000000000 | 15 |
TransitionLane11 | 65536 | 0000000000000010000000000000000 | 16 |
TransitionLane12 | 131072 | 0000000000000100000000000000000 | 17 |
TransitionLane13 | 262144 | 0000000000001000000000000000000 | 18 |
TransitionLane14 | 524288 | 0000000000010000000000000000000 | 19 |
TransitionLane15 | 1048576 | 0000000000100000000000000000000 | 20 |
TransitionLane16 | 2097152 | 0000000001000000000000000000000 | 21 |
RetryLane1 | 4194304 | 0000000010000000000000000000000 | 22 |
RetryLane2 | 8388608 | 0000000100000000000000000000000 | 23 |
RetryLane3 | 16777216 | 0000001000000000000000000000000 | 24 |
RetryLane4 | 33554432 | 0000010000000000000000000000000 | 25 |
RetryLane5 | 67108864 | 0000100000000000000000000000000 | 26 |
SelectiveHydrationLane | 134217728 | 0001000000000000000000000000000 | 27 |
IdleHydrationLane | 268435456 | 0010000000000000000000000000000 | 28 |
IdleLane | 536870912 | 0100000000000000000000000000000 | 29 |
OffscreenLane | 1073741824 | 1000000000000000000000000000000 | 30 |
event優(yōu)先級
EventPriority | ? | Lane | 數值 |
---|---|---|---|
DiscreteEventPriority | 離散事件。click、keydown、focusin等,事件的觸發(fā)不是連續(xù),可以做到快速響應 | SyncLane | 1 |
ContinuousEventPriority | 連續(xù)事件。drag、scroll、mouseover等,事件的是連續(xù)觸發(fā)的,快速響應可能會阻塞渲染,優(yōu)先級較離散事件低 | InputContinuousLane | 4 |
DefaultEventPriority | 默認的事件優(yōu)先級 | DefaultLane | 16 |
IdleEventPriority | 空閑的優(yōu)先級 | IdleLane | 536870912 |
scheduler優(yōu)先級
SchedulerPriority | EventPriority | 大于>17.0.2 | 小于>17.0.2 |
---|---|---|---|
ImmediatePriority | DiscreteEventPriority | 1 | 99 |
UserblockingPriority | Userblocking | 2 | 98 |
NormalPriority | DefaultEventPriority | 3 | 97 |
LowPriority | DefaultEventPriority | 4 | 96 |
IdlePriority | IdleEventPriority | 5 | 95 |
NoPriority | ? | 0 | 90 |
優(yōu)先級間的轉換
lane優(yōu)先級 轉 event優(yōu)先級(參考 lanesToEventPriority
函數)
- 轉換規(guī)則:以區(qū)間的形式根據傳入的lane返回對應的 event 優(yōu)先級。比如傳入的優(yōu)先級不大于 Discrete 優(yōu)先級,就返回 Discrete 優(yōu)先級,以此類推
event優(yōu)先級 轉 scheduler優(yōu)先級(參考 ensureRootIsScheduled
函數)
- 轉換規(guī)則:可以參考上面scheduler優(yōu)先級表
event優(yōu)先級 轉 lane優(yōu)先級(參考 getEventPriority
函數)
- 轉換規(guī)則:對于非離散、連續(xù)的事件,會根據一定規(guī)則作轉換,具體課參考上面 event 優(yōu)先級表,
優(yōu)先級機制如何設計
說到優(yōu)先級機制,我們可能馬上能聯想到的是優(yōu)先級隊列,其最突出的特性是最高優(yōu)先級先出,react
的優(yōu)先級機制跟優(yōu)先級隊列類似,不過其利用了賽道的概念,配合位與運算豐富了隊列的功能,比起優(yōu)先級隊列,讀寫速度更快,更加容易理解
設計思路
- 合并賽道:維護一個隊列,可以存儲被占用的賽道
- 釋放賽道:根據優(yōu)先級釋放對應被占用賽道
- 找出最高優(yōu)先級賽道:獲取隊列中最高優(yōu)先級賽道
- 快速定位賽道索引:根據優(yōu)先級獲取賽道在隊列中所在的位置
- 判斷賽道是否被占用:根據傳入優(yōu)先級判斷該優(yōu)先級所在賽道是否被占用
合并賽道
場景
- 比如當前正在調度的任務優(yōu)先級是DefaultLane,用戶點擊觸發(fā)更新,有一個高優(yōu)先級的任務SyncLane產生,需要存儲這個任務所占用的賽道
運算過程
- 運算方式:位或運算 -
a | b
- 運算結果:DefaultLane和SyncLane分別占用了第1條和第5條賽道
DefaultLane優(yōu)先級為16,SyncLane優(yōu)先級為1
16 | 1 = 17
17的二進制值為10001
16的二進制值為10000,1的二進制值為00001
釋放賽道
場景
- SyncLane 任務執(zhí)行完,需要釋放占用的賽道
運算過程
- 運算方式:位與+位非 -
a & ~b
- 運算結果:SyncLane賽道被釋放,只剩下DefaultLane賽道
17 & ~1 = 16
17的二進制值為10001為什么用位非?
~1 = -2
2 的二進制是00010,-2的話符號位取反變?yōu)?0010
10001和10010進行位與運算得到10000,也就是十進制的16
找出最高優(yōu)先級賽道
場景
- 當前有 DefaultLane 和 SyncLane 兩個優(yōu)先級的任務占用賽道,在進入 ensureRootIsScheduled 方法后,我需要先調度優(yōu)先級最高的任務,所以需要找出當前優(yōu)先級最高的賽道
運算過程
- 運算方式:位與+符號位取反 -
a & -b
- 運算結果:找到了最高優(yōu)先級的任務SyncLane,SyncLane任務為同步任務,Scheduler將以同步優(yōu)先級調度當前應用根節(jié)點
17 & -17 = 1
17的二進制值為10001
-17的二進制值為00001
10001和00001進行位與運算得到1,也就是SyncLane
快速定位賽道索引
場景
- 饑餓任務喚醒:在發(fā)起調度前,我們需要對隊列中的所有賽道進行一個判斷,判斷該賽道的任務是否過期,如果過期,就優(yōu)先執(zhí)行該過期任務。為此,需要維護一個長度為31的數組,數組的每個元素的下標索引與31個優(yōu)先級賽道一一對應,數組中存儲的是任務的過期時間,在判斷時,我們希望能根據優(yōu)先級快速找到該優(yōu)先級在數組中對應的位置。
運算過程
- 運算方式:Math.clz32
- 運算結果:找到了DefaultLane的索引位置為4,那就可以釋放應用根節(jié)點上的eventTimes、expirationTimes,將其所在位置的值賦值為-1,然后執(zhí)行對應的過期任務
// 找出 DefaultLane 賽道索引
31 - Math.clz32(16) = 416的二進制值為10000
索引4對應的就是第五個賽道
Math.clz32是用來干什么的?
- 獲取一個十進制數字對應二進制值中開頭0的個數。
- 所以用31減去
Math.clz32
的值就能得到該賽道的索引
判斷賽道是否被占用
異步模式下會存在高優(yōu)先級任務插隊的情況,此情況下 state
的計算方式會跟同步模式下**有些不同。
場景
我們 setState 之后并不是馬上就會更新 state
,而是會根據 setState 的內容生成一個 Update
對象,這個對象包含了更新內容、更新優(yōu)先級等屬性。
更新 state
這個動作是在 processUpdateQueue
函數里進行的,函數里面會判斷 Update
對象的優(yōu)先級所在賽道是否被占用,來決定是否在此輪任務中計算這個 Update
對象的 state
- 如果被占用,代表
Update
對象優(yōu)先級和當前正在進行的任務相等,可以根據Update
對象計算state
并更新到 Fiber 節(jié)點的memoizedState
屬性上 - 如果未被占用,代表當前正在進行的任務優(yōu)先級比這個
Update
對象優(yōu)先級高,相應的這個低優(yōu)先級的Update
對象將暫不被計算state,留到下一輪低優(yōu)先級任務被重啟時再進行計算
運算過程
- 運算方式:位與
(renderLanes & updateLanes) == updateLanes
- 運算結果:0代表當前調度優(yōu)先級高于某個Update對象優(yōu)先級
運算公式
(1 & 16) == 161的二進制值為00001
16的二進制值為10000?
00001和10000進行位與運算得到0
如何將優(yōu)先級機制融入React運行時
生成一個更新任務
生成任務的流程其實非常簡單,入口就在我們常用的 setState
函數,先上圖
setState
函數內部執(zhí)行的就是 enqueueUpdate
函數,而 enqueueUpdate
函數的工作主要分為4步:
- 獲取本次更新的優(yōu)先級。
- 創(chuàng)建
Update
對象 - 將本次更新優(yōu)先級關聯到當前Fiber節(jié)點、父級節(jié)點和應用根節(jié)點
- 發(fā)起
ensureRootIsScheduled
調度。
步驟一:獲取本次更新的優(yōu)先級
步驟一的工作是調用 requestUpdateLane
函數拿到此次更新任務的優(yōu)先級
如果當前為非 concurrent
模式
- 當前不在 render 階段。返回 syncLane
- 當前正在 render 階段。返回 workInProgressRootRenderLanes 中最高的優(yōu)先級(這里就用到上面的優(yōu)先級運算機制,找出最高優(yōu)先級賽道)
如果當前為 concurrent
模式
- 需要執(zhí)行延遲任務的話,比如
Suspend
、useTransition
、useDefferedValue
等特性。在transition
類型的優(yōu)先級中尋找空閑的賽道。transition
類型的賽道有 16 條,從第 1 條到第 16 條,當到達第 16 條賽道后,下一次transition
類型的任務會回到第 1 條賽道,如此往復。 - 執(zhí)行
getCurrentUpdatePriority
函數。獲取當前更新優(yōu)先級。如果不為NoLane
就返回 - 執(zhí)行
getCurrentEventPriority
函數。返回當前的事件優(yōu)先級。如果沒有事件產生,返回DefaultEventPriority
總的來說,requestUpdateLane
函數的優(yōu)先級選取判斷順序如下:
SyncLane >> TransitionLane >> UpdateLane >> EventLane
估計有很多小伙伴都會很困惑一個問題,為什么會有這么多獲取優(yōu)先級的函數,這里我整理了一下其他函數的職責
步驟二:創(chuàng)建 Update 對象
這里的代碼量不多,其實就是將 setState 的參數用一個對象封裝起來,留給 render 階段用
function createUpdate(eventTime, lane) { var update = { eventTime: eventTime, lane: lane, tag: UpdateState, payload: null, callback: null, next: null }; return update; }
步驟三:關聯優(yōu)先級
在這里先解釋兩個概念,一個是 HostRoot
,一個是 FiberRootNode
-
HostRoot
:就是ReactDOM.render
的第一個參數,組件樹的根節(jié)點。HostRoot
可能會存在多個,因為ReactDOM.render
可以多次調用 -
FiberRootNode
:react 的應用根節(jié)點,每個頁面只有一個 react 的應用根節(jié)點。可以從HostRoot
節(jié)點的stateNode
屬性訪問
這里關聯優(yōu)先級主要執(zhí)行了兩個函數
markUpdateLaneFromFiberToRoot
。該函數主要做了兩個事情
- 將優(yōu)先級合并到當前 Fiber 節(jié)點的 lanes 屬性中
- 將優(yōu)先級合并到父級節(jié)點的 childLanes 屬性中(告訴父節(jié)點他的子節(jié)點有多少條賽道要跑) 但因為函數傳入的 Fiber 節(jié)點是
HostRoot
,也就是ReactDOM.render
的根節(jié)點,也就是說沒有父節(jié)點了,所以第二件事情沒有做
markRootUpdated
。該函數也是主要做了兩個事情
- 將待調度任務優(yōu)先級合并到當前 react 應用根節(jié)點上
- 計算當前任務優(yōu)先級賽道占用的開始時間(eventTime)
由此可見,react
的優(yōu)先級機制并不獨立運行在每一個組件節(jié)點里面,而是依賴一個全局的 react
應用根節(jié)點去控制下面多個組件樹的任務調度
將優(yōu)先級關聯到這些Fiber節(jié)點有什么用?
先說說他們的區(qū)別
- lanes:只存在非 react 應用根節(jié)點上,記錄當前 Fiber 節(jié)點的 lane 優(yōu)先級
- childLanes:只存在非 react 應用根節(jié)點上,記錄當前 Fiber 節(jié)點下的所有子 Fiber 節(jié)點的 lane 優(yōu)先級
- pendingLanes:只存在 react 應用根節(jié)點上,記錄的是所有
HostRoot
的 lane 優(yōu)先級
具體應用場景
- 釋放賽道。上面說的優(yōu)先級運算機制提到了任務執(zhí)行完畢會釋放賽道,具體來說是在 commit 階段結束之后釋放被占用的優(yōu)先級,也就是
markRootFinished
函數。 - 判斷賽道是否被占用。在 render 階段的
beginWork
流程里面,會有很多判斷childLanes
是否被占用的判斷
步驟四:發(fā)起調度
調度里面最關鍵的一步,就是 ensureRootIsScheduled
函數的調用,該函數的邏輯就是由下面兩大部分構成,高優(yōu)先級任務打斷低優(yōu)先級任務 和 饑餓任務問題
高優(yōu)先級任務打斷低優(yōu)先級任務
該部分流程可以分為三部曲
- cancelCallback
- pop(taskQueue)
- 低優(yōu)先級任務重啟
cancelCallback
var existingCallbackNode = root.callbackNode; var existingCallbackPriority = root.callbackPriority; var newCallbackPriority = getHighestPriorityLane(nextLanes); if (existingCallbackPriority === newCallbackPriority) { ... return; } if (existingCallbackNode != null) { cancelCallback(existingCallbackNode); } newCallbackNode = scheduleCallback( schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root) ); root.callbackPriority = newCallbackPriority; root.callbackNode = newCallbackNode;
上面是 ensureRootIsScheduled
函數的一些代碼片段,先對變量做解釋
existingCallbackNode
:當前 render 階段正在進行的任務
existingCallbackPriority
:當前 render 階段正在進行的任務優(yōu)先級
newCallbackPriority
:此次調度優(yōu)先級
這里會判斷 existingCallbackPriority
和 newCallbackPriority
兩個優(yōu)先級是否相等,如果相等,此次更新合并到當前正在進行的任務中。如果不相等,代表此次更新任務的優(yōu)先級更高,需要打斷當前正在進行的任務
如何打斷任務?
- 關鍵函數
cancelCallback(existingCallbackNode)
,cancelCallback
函數就是將root.callbackNode
賦值為null -
performConcurrentWorkOnRoot
函數會先把root.callbackNode
緩存起來,在函數末尾會再判斷root.callbackNode
和開始緩存起來的值是否一樣,如果不一樣,就代表root.callbackNode
被賦值為null了,有更高優(yōu)先級任務進來。 - 此時
performConcurrentWorkOnRoot
返回值為null
下面是 performConcurrentWorkOnRoot
代碼片段
... var originalCallbackNode = root.callbackNode; ... // 函數末尾 if (root.callbackNode === originalCallbackNode) { return performConcurrentWorkOnRoot.bind(null, root); } return null;
由上面 ensureRootIsScheduled
的代碼片段可以知道,performConcurrentWorkOnRoot
函數是被 scheduleCallback
函數調度的,具體返回后的邏輯需要到 Scheduler
模塊去找
pop(taskQueue)
var callback = currentTask.callback; if (typeof callback === 'function') { ... } else { pop(taskQueue); }
上面是 Scheduler
模塊里面 workLoop
函數的代碼片段,currentTask.callback
就是 scheduleCallback
的第二個參數,也就是performConcurrentWorkOnRoot
函數
承接上個主題,如果 performConcurrentWorkOnRoot
函數返回了null,workLoop
內部就會執(zhí)行 pop(taskQueue)
,將當前的任務從 taskQueue
中彈出。
低優(yōu)先級任務重啟
上一步中說道一個低優(yōu)先級任務從 taskQueue
中被彈出。那高優(yōu)先級任務執(zhí)行完畢之后,如何重啟回之前的低優(yōu)先級任務呢?
關鍵是在 commitRootImpl
函數
var remainingLanes = mergeLanes(finishedWork.lanes, finishedWork.childLanes); markRootFinished(root, remainingLanes); ... ensureRootIsScheduled(root, now());
markRootFinished
函數剛剛上面說了是釋放已完成任務所占用的賽道,那也就是說未完成任務依然會占用其賽道,所以我們可以重新調用 ensureRootIsScheduled
發(fā)起一次新的調度,去重啟低優(yōu)先級任務的執(zhí)行。我們可以看下重啟部分的判斷
var nextLanes = getNextLanes( root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes ); // 如果 nextLanes 為 NoLanes,就證明所有任務都執(zhí)行完畢了 if (nextLanes === NoLanes) { ... root.callbackNode = null; root.callbackPriority = NoLane; // 只要 nextLanes 為 NoLanes,就可以結束調度了 return; } // 如果 nextLanes 不為 NoLanes,就代表還有任務未執(zhí)行完,也就是那些被打斷的低優(yōu)先級任務 ...
饑餓任務問題
上面說到,在高優(yōu)先級任務執(zhí)行完畢之后,低優(yōu)先級任務就會被重啟,但假設如果持續(xù)有高優(yōu)先級任務持續(xù)進來,我的低優(yōu)先級任務豈不是沒有重啟之日?
所以 react 為了處理解決饑餓任務問題,react 在 ensureRootIsScheduled
函數開始的時候做了以下處理:(參考markStarvedLanesAsExpired
函數)
var lanes = pendingLanes; while (lanes > 0) { var index = pickArbitraryLaneIndex(lanes); var lane = 1 << index; var expirationTime = expirationTimes[index]; if (expirationTime === NoTimestamp) { if ((lane & suspendedLanes) === NoLanes || (lane & pingedLanes) !== NoLanes) { expirationTimes[index] = computeExpirationTime(lane, currentTime); } } else if (expirationTime <= currentTime) { root.expiredLanes |= lane; } lanes &= ~lane; }
- 遍歷31條賽道,判斷每條賽道的過期時間是否為
NoTimestamp
,如果是,且該賽道存在待執(zhí)行的任務,則為該賽道初始化過期時間 - 如果該賽道已存在過期時間,且過期時間已經小于當前時間,則代表任務已過期,需要將當前優(yōu)先級合并到
expiredLanes
,這樣在下一輪 render 階段就會以同步優(yōu)先級調度當前HostRoot
可以參考 render 階段執(zhí)行的函數 performConcurrentWorkOnRoot
中的代碼片段
var exitStatus = shouldTimeSlice(root, lanes) && ( !didTimeout) ? renderRootConcurrent(root, lanes) : renderRootSync(root, lanes);
可以看到只要 shouldTimeSlice
只要返回 false,就會執(zhí)行 renderRootSync
,也就是以同步優(yōu)先級進入 render 階段。而 shouldTimeSlice
的邏輯也就是剛剛的 expiredLanes
屬性相關
function shouldTimeSlice(root, lanes) { // 如果 expiredLanes 里面有東西,代表有饑餓任務 if ((lanes & root.expiredLanes) !== NoLanes) { return false; } var SyncDefaultLanes = InputContinuousHydrationLane | InputContinuousLane | DefaultHydrationLane | DefaultLane; return (lanes & SyncDefaultLanes) === NoLanes; }
總結
react
的優(yōu)先級機制在源碼中并不是一個獨立的,解耦的模塊,而是涉及到了react整體運行的方方面面,最后回歸整理下優(yōu)先級機制在源碼中的使用,讓大家對優(yōu)先級機制有一個更加整體的認知。
- 時間分片。涉及到任務打斷、根據優(yōu)先級計算分片時長
- setState 生成 Update 對象。每個 Update 對象里面都有一個 lane 屬性,代表此次更新的優(yōu)先級
- 高優(yōu)先級任務打斷低優(yōu)先級任務。每一次調度都會對正在進行任務和當前任務最高優(yōu)先級做比較,如果不相等,就代表有高優(yōu)先級任務進來,需要打斷當前正在的任務。
- 低優(yōu)先級任務重啟。協調
(reconcile)
的下一個階段是渲染(renderer)
,也就是我們說的 commit 階段,在此階段末尾,會調用ensureRootIsScheduled
發(fā)起一次新的調度,執(zhí)行尚未完成的低優(yōu)先級任務。 - 饑餓任務喚醒。每次調度的開始,都會先檢查下有沒有過期任務,如果有的話,下一次就會以同步優(yōu)先級進行 render 任務
(reconcile)
,同步優(yōu)先級就是最高的優(yōu)先級,不會被打斷
原文鏈接:https://juejin.cn/post/7163447394493792264
相關推薦
- 2022-04-24 25個值得收藏的Python文本處理案例_python
- 2022-06-30 基于Python使用永中文檔轉換服務的方式_python
- 2022-04-26 JQuery實現電梯導航特效_jquery
- 2022-11-21 Golang?Mutex互斥鎖源碼分析_Golang
- 2022-08-12 python封裝成exe的超詳細教程_python
- 2022-04-28 Python的命令行參數實例詳解_python
- 2022-08-19 存儲引擎的應用場景
- 2022-06-30 利用Python刪除電腦中重復文件的方法_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發(fā)現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支