UI產生交互的根本緣由是各類事件,這也就意味着事件與更新有着直接關係。不一樣事件產生的更新,它們的優先級是有差別的,因此更新優先級的根源在於事件的優先級。一個更新的產生可直接致使React生成一個更新任務,最終這個任務被Scheduler調度。javascript
因此在React中,人爲地將事件劃分了等級,最終目的是決定調度任務的輕重緩急,所以,React有一套從事件到調度的優先級機制。java
本文將圍繞事件優先級、更新優先級、任務優先級、調度優先級,重點梳理它們之間的轉化關係。react
前三者屬於React的優先級機制,第四個屬於Scheduler的優先級機制,Scheduler內部有本身的優先級機制,雖然與React有所區別,但等級的劃分基本一致。下面咱們從事件優先級開始提及。git
React按照事件的緊急程度,把它們劃分紅三個等級:github
事件優先級是在註冊階段被肯定的,在向root上註冊事件時,會根據事件的類別,建立不一樣優先級的事件監聽(listener),最終將它綁定到root上去。app
let listener = createEventListenerWrapperWithPriority( targetContainer, domEventName, eventSystemFlags, listenerPriority, );
createEventListenerWrapperWithPriority
函數的名字已經把它作的事情交代得八九不離十了。它會首先根據事件的名稱去找對應的事件優先級,而後依據優先級返回不一樣的事件監聽函數。dom
export function createEventListenerWrapperWithPriority( targetContainer: EventTarget, domEventName: DOMEventName, eventSystemFlags: EventSystemFlags, priority?: EventPriority, ): Function { const eventPriority = priority === undefined ? getEventPriorityForPluginSystem(domEventName) : priority; let listenerWrapper; switch (eventPriority) { case DiscreteEvent: listenerWrapper = dispatchDiscreteEvent; break; case UserBlockingEvent: listenerWrapper = dispatchUserBlockingUpdate; break; case ContinuousEvent: default: listenerWrapper = dispatchEvent; break; } return listenerWrapper.bind( null, domEventName, eventSystemFlags, targetContainer, ); }
最終綁定到root上的事件監聽實際上是dispatchDiscreteEvent、dispatchUserBlockingUpdate、dispatchEvent這三個中的一個。它們作的事情都是同樣的,以各自的事件優先級去執行真正的事件處理函數。函數
好比:dispatchDiscreteEvent
和dispatchUserBlockingUpdate
最終都會以UserBlockingEvent的事件級別去執行事件處理函數。oop
以某種優先級去執行事件處理函數其實要藉助Scheduler中提供的runWithPriority
函數來實現:spa
function dispatchUserBlockingUpdate( domEventName, eventSystemFlags, container, nativeEvent, ) { ... runWithPriority( UserBlockingPriority, dispatchEvent.bind( null, domEventName, eventSystemFlags, container, nativeEvent, ), ); ... }
這麼作能夠將事件優先級記錄到Scheduler中,至關於告訴Scheduler:你幫我記錄一下當前事件派發的優先級,等React那邊建立更新對象(即update)計算更新優先級時直接從你這拿就行了。
function unstable_runWithPriority(priorityLevel, eventHandler) { switch (priorityLevel) { case ImmediatePriority: case UserBlockingPriority: case NormalPriority: case LowPriority: case IdlePriority: break; default: priorityLevel = NormalPriority; } var previousPriorityLevel = currentPriorityLevel; // 記錄優先級到Scheduler內部的變量裏 currentPriorityLevel = priorityLevel; try { return eventHandler(); } finally { currentPriorityLevel = previousPriorityLevel; } }
以setState爲例,事件的執行會致使setState執行,而setState本質上是調用enqueueSetState,生成一個update對象,這時候會計算它的更新優先級,即update.lane:
const classComponentUpdater = { enqueueSetState(inst, payload, callback) { ... // 依據事件優先級建立update的優先級 const lane = requestUpdateLane(fiber, suspenseConfig); const update = createUpdate(eventTime, lane, suspenseConfig); update.payload = payload; enqueueUpdate(fiber, update); // 開始調度 scheduleUpdateOnFiber(fiber, lane, eventTime); ... }, };
重點關注requestUpdateLane,它首先找出Scheduler中記錄的優先級:schedulerPriority,而後計算更新優先級:lane,具體的計算過程在findUpdateLane函數中,計算過程是一個從高到低依次佔用空閒位的操做,具體的代碼在這裏 ,這裏就先不詳細展開。
export function requestUpdateLane( fiber: Fiber, suspenseConfig: SuspenseConfig | null, ): Lane { ... // 根據記錄下的事件優先級,獲取任務調度優先級 const schedulerPriority = getCurrentPriorityLevel(); let lane; if ( (executionContext & DiscreteEventContext) !== NoContext && schedulerPriority === UserBlockingSchedulerPriority ) { // 若是事件優先級是用戶阻塞級別,則直接用InputDiscreteLanePriority去計算更新優先級 lane = findUpdateLane(InputDiscreteLanePriority, currentEventWipLanes); } else { // 依據事件的優先級去計算schedulerLanePriority const schedulerLanePriority = schedulerPriorityToLanePriority( schedulerPriority, ); ... // 根據事件優先級計算得來的schedulerLanePriority,去計算更新優先級 lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes); } return lane; }
getCurrentPriorityLevel
負責讀取記錄在Scheduler中的優先級:
function unstable_getCurrentPriorityLevel() { return currentPriorityLevel; }
update對象建立完成後意味着須要對頁面進行更新,會調用scheduleUpdateOnFiber進入調度,而真正開始調度以前會計算本次產生的更新任務的任務優先級,目的是與已有任務的任務優先級去作比較,便於作出多任務的調度決策。
調度決策的邏輯在ensureRootIsScheduled 函數中,這是一個很是重要的函數,控制着React任務進入Scheduler的大門。
一個update會被一個React的更新任務執行掉,任務優先級被用來區分多個更新任務的緊急程度,它由更新優先級計算而來,舉例來講:
假設產生一前一後兩個update,它們持有各自的更新優先級,也會被各自的更新任務執行。通過優先級計算,若是後者的任務優先級高於前者的任務優先級,那麼會讓Scheduler取消前者的任務調度;若是後者的任務優先級等於前者的任務優先級,後者不會致使前者被取消,而是會複用前者的更新任務,將兩個同等優先級的更新收斂到一次任務中;若是後者的任務優先級低於前者的任務優先級,一樣不會致使前者的任務被取消,而是在前者更新完成後,再次用Scheduler對後者發起一次任務調度。
這是任務優先級存在的意義,保證高優先級任務及時響應,收斂同等優先級的任務調度。
任務優先級在即將調度的時候去計算,代碼在ensureRootIsScheduled
函數中:
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) { ... // 獲取nextLanes,順便計算任務優先級 const nextLanes = getNextLanes( root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes, ); // 獲取上面計算得出的任務優先級 const newCallbackPriority = returnNextLanesPriority(); ... }
經過調用getNextLanes去計算在本次更新中應該處理的這批lanes(nextLanes),getNextLanes會調用getHighestPriorityLanes去計算任務優先級。任務優先級計算的原理是這樣:更新優先級(update的lane),
它會被併入root.pendingLanes,root.pendingLanes通過getNextLanes處理後,挑出那些應該處理的lanes,傳入getHighestPriorityLanes
,根據nextLanes找出這些lanes的優先級做爲任務優先級。
function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes { ... // 都是這種比較賦值的過程,這裏只保留兩個以作簡要說明 const inputDiscreteLanes = InputDiscreteLanes & lanes; if (inputDiscreteLanes !== NoLanes) { return_highestLanePriority = InputDiscreteLanePriority; return inputDiscreteLanes; } if ((lanes & InputContinuousHydrationLane) !== NoLanes) { return_highestLanePriority = InputContinuousHydrationLanePriority; return InputContinuousHydrationLane; } ... return lanes; }
getHighestPriorityLanes的源碼在這裏,getNextLanes的源碼在這裏
return_highestLanePriority
就是任務優先級,它有以下這些值,值越大,優先級越高,暫時只理解任務優先級的做用便可。
export const SyncLanePriority: LanePriority = 17; export const SyncBatchedLanePriority: LanePriority = 16; const InputDiscreteHydrationLanePriority: LanePriority = 15; export const InputDiscreteLanePriority: LanePriority = 14; const InputContinuousHydrationLanePriority: LanePriority = 13; export const InputContinuousLanePriority: LanePriority = 12; const DefaultHydrationLanePriority: LanePriority = 11; export const DefaultLanePriority: LanePriority = 10; const TransitionShortHydrationLanePriority: LanePriority = 9; export const TransitionShortLanePriority: LanePriority = 8; const TransitionLongHydrationLanePriority: LanePriority = 7; export const TransitionLongLanePriority: LanePriority = 6; const RetryLanePriority: LanePriority = 5; const SelectiveHydrationLanePriority: LanePriority = 4; const IdleHydrationLanePriority: LanePriority = 3; const IdleLanePriority: LanePriority = 2; const OffscreenLanePriority: LanePriority = 1; export const NoLanePriority: LanePriority = 0;
若是已經存在一個更新任務,ensureRootIsScheduled
會在獲取到新任務的任務優先級以後,去和舊任務的任務優先級去比較,從而作出是否須要從新發起調度的決定,若須要發起調度,那麼會去計算調度優先級。
一旦任務被調度,那麼它就會進入Scheduler,在Scheduler中,這個任務會被包裝一下,生成一個屬於Scheduler本身的task,這個task持有的優先級就是調度優先級。
它有什麼做用呢?在Scheduler中,分別用過時任務隊列和未過時任務的隊列去管理它內部的task,過時任務的隊列中的task根據過時時間去排序,最先過時的排在前面,便於被最早處理。而過時時間是由調度優先級計算的出的,不一樣的調度優先級對應的過時時間不一樣。
調度優先級由任務優先級計算得出,在ensureRootIsScheduled
更新真正讓Scheduler發起調度的時候,會去計算調度優先級。
function ensureRootIsScheduled(root: FiberRoot, currentTime: number) { ... // 根據任務優先級獲取Scheduler的調度優先級 const schedulerPriorityLevel = lanePriorityToSchedulerPriority( newCallbackPriority, ); // 計算出調度優先級以後,開始讓Scheduler調度React的更新任務 newCallbackNode = scheduleCallback( schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root), ); ... }
lanePriorityToSchedulerPriority
計算調度優先級的過程是根據任務優先級找出對應的調度優先級。
export function lanePriorityToSchedulerPriority( lanePriority: LanePriority, ): ReactPriorityLevel { switch (lanePriority) { case SyncLanePriority: case SyncBatchedLanePriority: return ImmediateSchedulerPriority; case InputDiscreteHydrationLanePriority: case InputDiscreteLanePriority: case InputContinuousHydrationLanePriority: case InputContinuousLanePriority: return UserBlockingSchedulerPriority; case DefaultHydrationLanePriority: case DefaultLanePriority: case TransitionShortHydrationLanePriority: case TransitionShortLanePriority: case TransitionLongHydrationLanePriority: case TransitionLongLanePriority: case SelectiveHydrationLanePriority: case RetryLanePriority: return NormalSchedulerPriority; case IdleHydrationLanePriority: case IdleLanePriority: case OffscreenLanePriority: return IdleSchedulerPriority; case NoLanePriority: return NoSchedulerPriority; default: invariant( false, 'Invalid update priority: %s. This is a bug in React.', lanePriority, ); } }
本文一共提到了4種優先級:事件優先級、更新優先級、任務優先級、調度優先級,它們之間是遞進的關係。事件優先級由事件自己決定,更新優先級由事件計算得出,而後放到root.pendingLanes,任務優先級來自root.pendingLanes中最緊急的那些lanes對應的優先級,調度優先級根據任務優先級獲取。幾種優先級環環相扣,保證了高優任務的優先執行。