日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
帶你了解React中的優(yōu)先級

 UI產(chǎn)生交互的根本原因是各種事件,這也就意味著事件與更新有著直接關(guān)系。不同事件產(chǎn)生的更新,它們的優(yōu)先級是有差異的,所以更新優(yōu)先級的根源在于事件的優(yōu)先級。一個(gè)更新的產(chǎn)生可直接導(dǎo)致React生成一個(gè)更新任務(wù),最終這個(gè)任務(wù)被Scheduler調(diào)度。

所以在React中,人為地將事件劃分了等級,最終目的是決定調(diào)度任務(wù)的輕重緩急,因此,React有一套從事件到調(diào)度的優(yōu)先級機(jī)制。

本文將圍繞事件優(yōu)先級、更新優(yōu)先級、任務(wù)優(yōu)先級、調(diào)度優(yōu)先級,重點(diǎn)梳理它們之間的轉(zhuǎn)化關(guān)系。

  •  事件優(yōu)先級:按照用戶事件的交互緊急程度,劃分的優(yōu)先級
  •  更新優(yōu)先級:事件導(dǎo)致React產(chǎn)生的更新對象(update)的優(yōu)先級(update.lane)
  •  任務(wù)優(yōu)先級:產(chǎn)生更新對象之后,React去執(zhí)行一個(gè)更新任務(wù),這個(gè)任務(wù)所持有的優(yōu)先級
  •  調(diào)度優(yōu)先級:Scheduler依據(jù)React更新任務(wù)生成一個(gè)調(diào)度任務(wù),這個(gè)調(diào)度任務(wù)所持有的優(yōu)先級

前三者屬于React的優(yōu)先級機(jī)制,第四個(gè)屬于Scheduler的優(yōu)先級機(jī)制,Scheduler內(nèi)部有自己的優(yōu)先級機(jī)制,雖然與React有所區(qū)別,但等級的劃分基本一致。下面我們從事件優(yōu)先級開始說起。

優(yōu)先級的起點(diǎn):事件優(yōu)先級

React按照事件的緊急程度,把它們劃分成三個(gè)等級:

  •  離散事件(DiscreteEvent):click、keydown、focusin等,這些事件的觸發(fā)不是連續(xù)的,優(yōu)先級為0。
  •  用戶阻塞事件(UserBlockingEvent):drag、scroll、mouseover等,特點(diǎn)是連續(xù)觸發(fā),阻塞渲染,優(yōu)先級為1。
  •  連續(xù)事件(ContinuousEvent):canplay、error、audio標(biāo)簽的timeupdate和canplay,優(yōu)先級最高,為2。

事件優(yōu)先級的Map

派發(fā)事件優(yōu)先級

事件優(yōu)先級是在注冊階段被確定的,在向root上注冊事件時(shí),會(huì)根據(jù)事件的類別,創(chuàng)建不同優(yōu)先級的事件監(jiān)聽(listener),最終將它綁定到root上去。

 
 
 
 
  1. let listener = createEventListenerWrapperWithPriority( 
  2.     targetContainer, 
  3.     domEventName, 
  4.     eventSystemFlags, 
  5.     listenerPriority, 
  6.   );

createEventListenerWrapperWithPriority函數(shù)的名字已經(jīng)把它做的事情交代得八九不離十了。它會(huì)首先根據(jù)事件的名稱去找對應(yīng)的事件優(yōu)先級,然后依據(jù)優(yōu)先級返回不同的事件監(jiān)聽函數(shù)。

 
 
 
 
  1. export function createEventListenerWrapperWithPriority( 
  2.   targetContainer: EventTarget, 
  3.   domEventName: DOMEventName, 
  4.   eventSystemFlags: EventSystemFlags, 
  5.   priority?: EventPriority,
  6. ): Function { 
  7.   const eventPriority = 
  8.     priority === undefined 
  9.       ? getEventPriorityForPluginSystem(domEventName) 
  10.       : priority; 
  11.   let listenerWrapper; 
  12.   switch (eventPriority) { 
  13.     case DiscreteEvent: 
  14.       listenerWrapper = dispatchDiscreteEvent; 
  15.       break; 
  16.     case UserBlockingEvent: 
  17.       listenerWrapper = dispatchUserBlockingUpdate; 
  18.       break; 
  19.     case ContinuousEvent: 
  20.     default: 
  21.       listenerWrapper = dispatchEvent; 
  22.       break; 
  23.   } 
  24.   return listenerWrapper.bind( 
  25.     null, 
  26.     domEventName, 
  27.     eventSystemFlags, 
  28.     targetContainer, 
  29.   ); 
  30. }

最終綁定到root上的事件監(jiān)聽其實(shí)是dispatchDiscreteEvent、dispatchUserBlockingUpdate、dispatchEvent這三個(gè)中的一個(gè)。它們做的事情都是一樣的,以各自的事件優(yōu)先級去執(zhí)行真正的事件處理函數(shù)。

比如:dispatchDiscreteEvent和dispatchUserBlockingUpdate最終都會(huì)以UserBlockingEvent的事件級別去執(zhí)行事件處理函數(shù)。

以某種優(yōu)先級去執(zhí)行事件處理函數(shù)其實(shí)要借助Scheduler中提供的runWithPriority函數(shù)來實(shí)現(xiàn):

 
 
 
 
  1. function dispatchUserBlockingUpdate( 
  2.   domEventName, 
  3.   eventSystemFlags, 
  4.   container, 
  5.   nativeEvent, 
  6. ) { 
  7.   ... 
  8.   runWithPriority( 
  9.     UserBlockingPriority, 
  10.     dispatchEvent.bind( 
  11.       null, 
  12.       domEventName, 
  13.       eventSystemFlags, 
  14.       container, 
  15.       nativeEvent, 
  16.     ), 
  17.   ); 
  18.   ... 
  19. }

這么做可以將事件優(yōu)先級記錄到Scheduler中,相當(dāng)于告訴Scheduler:你幫我記錄一下當(dāng)前事件派發(fā)的優(yōu)先級,等React那邊創(chuàng)建更新對象(即update)計(jì)算更新優(yōu)先級時(shí)直接從你這拿就好了。

 
 
 
 
  1. function unstable_runWithPriority(priorityLevel, eventHandler) { 
  2.   switch (priorityLevel) { 
  3.     case ImmediatePriority: 
  4.     case UserBlockingPriority: 
  5.     case NormalPriority:
  6.      case LowPriority: 
  7.     case IdlePriority: 
  8.       break; 
  9.     default: 
  10.       priorityLevel = NormalPriority; 
  11.   } 
  12.   var previousPriorityLevel = currentPriorityLevel; 
  13.   // 記錄優(yōu)先級到Scheduler內(nèi)部的變量里 
  14.   currentPriorityLevel = priorityLevel; 
  15.   try { 
  16.     return eventHandler(); 
  17.   } finally { 
  18.     currentPriorityLevel = previousPriorityLevel; 
  19.   } 
  20. }

更新優(yōu)先級

以setState為例,事件的執(zhí)行會(huì)導(dǎo)致setState執(zhí)行,而setState本質(zhì)上是調(diào)用enqueueSetState,生成一個(gè)update對象,這時(shí)候會(huì)計(jì)算它的更新優(yōu)先級,即update.lane:

 
 
 
 
  1. const classComponentUpdater = { 
  2.   enqueueSetState(inst, payload, callback) { 
  3.     ... 
  4.     // 依據(jù)事件優(yōu)先級創(chuàng)建update的優(yōu)先級 
  5.     const lane = requestUpdateLane(fiber, suspenseConfig); 
  6.     const update = createUpdate(eventTime, lane, suspenseConfig); 
  7.     update.payload = payload; 
  8.     enqueueUpdate(fiber, update); 
  9.     // 開始調(diào)度 
  10.     scheduleUpdateOnFiber(fiber, lane, eventTime); 
  11.     ... 
  12.   }, 
  13. };

重點(diǎn)關(guān)注requestUpdateLane,它首先找出Scheduler中記錄的優(yōu)先級:schedulerPriority,然后計(jì)算更新優(yōu)先級:lane,具體的計(jì)算過程在findUpdateLane函數(shù)中,計(jì)算過程是一個(gè)從高到低依次占用空閑位的操作,具體的代碼在這里 ,這里就先不詳細(xì)展開。

 
 
 
 
  1. export function requestUpdateLane( 
  2.   fiber: Fiber, 
  3.   suspenseConfig: SuspenseConfig | null, 
  4. ): Lane { 
  5.   ... 
  6.   // 根據(jù)記錄下的事件優(yōu)先級,獲取任務(wù)調(diào)度優(yōu)先級 
  7.   const schedulerPriority = getCurrentPriorityLevel(); 
  8.   let lane; 
  9.   if ( 
  10.     (executionContext & DiscreteEventContext) !== NoContext && 
  11.     schedulerPriority === UserBlockingSchedulerPriority 
  12.   ) { 
  13.     // 如果事件優(yōu)先級是用戶阻塞級別,則直接用InputDiscreteLanePriority去計(jì)算更新優(yōu)先級 
  14.     lane = findUpdateLane(InputDiscreteLanePriority, currentEventWipLanes); 
  15.   } else { 
  16.     // 依據(jù)事件的優(yōu)先級去計(jì)算schedulerLanePriority 
  17.     const schedulerLanePriority = schedulerPriorityToLanePriority( 
  18.       schedulerPriority, 
  19.     ); 
  20.     ... 
  21.     // 根據(jù)事件優(yōu)先級計(jì)算得來的schedulerLanePriority,去計(jì)算更新優(yōu)先級 
  22.     lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes); 
  23.   } 
  24.   return lane; 
  25. }

getCurrentPriorityLevel負(fù)責(zé)讀取記錄在Scheduler中的優(yōu)先級:

 
 
 
 
  1. function unstable_getCurrentPriorityLevel() { 
  2.   return currentPriorityLevel; 
  3. }

update對象創(chuàng)建完成后意味著需要對頁面進(jìn)行更新,會(huì)調(diào)用scheduleUpdateOnFiber進(jìn)入調(diào)度,而真正開始調(diào)度之前會(huì)計(jì)算本次產(chǎn)生的更新任務(wù)的任務(wù)優(yōu)先級,目的是與已有任務(wù)的任務(wù)優(yōu)先級去做比較,便于做出多任務(wù)的調(diào)度決策。

調(diào)度決策的邏輯在ensureRootIsScheduled 函數(shù)中,這是一個(gè)非常重要的函數(shù),控制著React任務(wù)進(jìn)入Scheduler的大門。

任務(wù)優(yōu)先級

一個(gè)update會(huì)被一個(gè)React的更新任務(wù)執(zhí)行掉,任務(wù)優(yōu)先級被用來區(qū)分多個(gè)更新任務(wù)的緊急程度,它由更新優(yōu)先級計(jì)算而來,舉例來說:

假設(shè)產(chǎn)生一前一后兩個(gè)update,它們持有各自的更新優(yōu)先級,也會(huì)被各自的更新任務(wù)執(zhí)行。經(jīng)過優(yōu)先級計(jì)算,如果后者的任務(wù)優(yōu)先級高于前者的任務(wù)優(yōu)先級,那么會(huì)讓Scheduler取消前者的任務(wù)調(diào)度;如果后者的任務(wù)優(yōu)先級等于前者的任務(wù)優(yōu)先級,后者不會(huì)導(dǎo)致前者被取消,而是會(huì)復(fù)用前者的更新任務(wù),將兩個(gè)同等優(yōu)先級的更新收斂到一次任務(wù)中;如果后者的任務(wù)優(yōu)先級低于前者的任務(wù)優(yōu)先級,同樣不會(huì)導(dǎo)致前者的任務(wù)被取消,而是在前者更新完成后,再次用Scheduler對后者發(fā)起一次任務(wù)調(diào)度。

這是任務(wù)優(yōu)先級存在的意義,保證高優(yōu)先級任務(wù)及時(shí)響應(yīng),收斂同等優(yōu)先級的任務(wù)調(diào)度。

任務(wù)優(yōu)先級在即將調(diào)度的時(shí)候去計(jì)算,代碼在ensureRootIsScheduled函數(shù)中:

 
 
 
 
  1. function ensureRootIsScheduled(root: FiberRoot, currentTime: number) { 
  2.   ... 
  3.   // 獲取nextLanes,順便計(jì)算任務(wù)優(yōu)先級 
  4.   const nextLanes = getNextLanes( 
  5.     root, 
  6.     root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes, 
  7.   ); 
  8.   // 獲取上面計(jì)算得出的任務(wù)優(yōu)先級 
  9.   const newCallbackPriority = returnNextLanesPriority(); 
  10.   ... 
  11. }

通過調(diào)用getNextLanes去計(jì)算在本次更新中應(yīng)該處理的這批lanes(nextLanes),getNextLanes會(huì)調(diào)用getHighestPriorityLanes去計(jì)算任務(wù)優(yōu)先級。任務(wù)優(yōu)先級計(jì)算的原理是這樣:更新優(yōu)先級(update的lane),它會(huì)被并入root.pendingLanes,root.pendingLanes經(jīng)過getNextLanes處理后,挑出那些應(yīng)該處理的lanes,傳入getHighestPriorityLanes,根據(jù)nextLanes找出這些lanes的優(yōu)先級作為任務(wù)優(yōu)先級。

 
 
 
 
  1. function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes {  ... 
  2.   // 都是這種比較賦值的過程,這里只保留兩個(gè)以做簡要說明 
  3.   const inputDiscreteLanes = InputDiscreteLanes & lanes; 
  4.   if (inputDiscreteLanes !== NoLanes) { 
  5.     return_highestLanePriority = InputDiscreteLanePriority; 
  6.     return inputDiscreteLanes; 
  7.   } 
  8.   if ((lanes & InputContinuousHydrationLane) !== NoLanes) {
  9.     return_highestLanePriority = InputContinuousHydrationLanePriority; 
  10.     return InputContinuousHydrationLane; 
  11.   } 
  12.   ... 
  13.   return lanes; 
  14. }

getHighestPriorityLanes的源碼在這里,getNextLanes的源碼在這里

return_highestLanePriority就是任務(wù)優(yōu)先級,它有如下這些值,值越大,優(yōu)先級越高,暫時(shí)只理解任務(wù)優(yōu)先級的作用即可。

 
 
 
 
  1. export const SyncLanePriority: LanePriority = 17; 
  2. export const SyncBatchedLanePriority: LanePriority = 16; 
  3. const InputDiscreteHydrationLanePriority: LanePriority = 15; 
  4. export const InputDiscreteLanePriority: LanePriority = 14; 
  5. const InputContinuousHydrationLanePriority: LanePriority = 13; 
  6. export const InputContinuousLanePriority: LanePriority = 12; 
  7. const DefaultHydrationLanePriority: LanePriority = 11; 
  8. export const DefaultLanePriority: LanePriority = 10; 
  9. const TransitionShortHydrationLanePriority: LanePriority = 9; 
  10. export const TransitionShortLanePriority: LanePriority = 8; 
  11. const TransitionLongHydrationLanePriority: LanePriority = 7; 
  12. export const TransitionLongLanePriority: LanePriority = 6; 
  13. const RetryLanePriority: LanePriority = 5; 
  14. const SelectiveHydrationLanePriority: LanePriority = 4; 
  15. const IdleHydrationLanePriority: LanePriority = 3; 
  16. const IdleLanePriority: LanePriority = 2; 
  17. const OffscreenLanePriority: LanePriority = 1; 
  18. export const NoLanePriority: LanePriority = 0;

如果已經(jīng)存在一個(gè)更新任務(wù),ensureRootIsScheduled會(huì)在獲取到新任務(wù)的任務(wù)優(yōu)先級之后,去和舊任務(wù)的任務(wù)優(yōu)先級去比較,從而做出是否需要重新發(fā)起調(diào)度的決定,若需要發(fā)起調(diào)度,那么會(huì)去計(jì)算調(diào)度優(yōu)先級。

調(diào)度優(yōu)先級

一旦任務(wù)被調(diào)度,那么它就會(huì)進(jìn)入Scheduler,在Scheduler中,這個(gè)任務(wù)會(huì)被包裝一下,生成一個(gè)屬于Scheduler自己的task,這個(gè)task持有的優(yōu)先級就是調(diào)度優(yōu)先級。

它有什么作用呢?在Scheduler中,分別用過期任務(wù)隊(duì)列和未過期任務(wù)的隊(duì)列去管理它內(nèi)部的task,過期任務(wù)的隊(duì)列中的task根據(jù)過期時(shí)間去排序,最早過期的排在前面,便于被最先處理。而過期時(shí)間是由調(diào)度優(yōu)先級計(jì)算得出的,不同的調(diào)度優(yōu)先級對應(yīng)的過期時(shí)間不同。

調(diào)度優(yōu)先級由任務(wù)優(yōu)先級計(jì)算得出,在ensureRootIsScheduled更新真正讓Scheduler發(fā)起調(diào)度的時(shí)候,會(huì)去計(jì)算調(diào)度優(yōu)先級。

 
 
 
 
  1. function ensureRootIsScheduled(root: FiberRoot, currentTime: number) { 
  2.     ... 
  3.     // 根據(jù)任務(wù)優(yōu)先級獲取Scheduler的調(diào)度優(yōu)先級 
  4.     const schedulerPriorityLevel = lanePriorityToSchedulerPriority( 
  5.       newCallbackPriority, 
  6.     ); 
  7.     // 計(jì)算出調(diào)度優(yōu)先級之后,開始讓Scheduler調(diào)度React的更新任務(wù) 
  8.     newCallbackNode = scheduleCallback( 
  9.       schedulerPriorityLevel, 
  10.       performConcurrentWorkOnRoot.bind(null, root), 
  11.     ); 
  12.     ...
  13. }

lanePriorityToSchedulerPriority計(jì)算調(diào)度優(yōu)先級的過程是根據(jù)任務(wù)優(yōu)先級找出對應(yīng)的調(diào)度優(yōu)先級。

 
 
 
 
  1. export function lanePriorityToSchedulerPriority( 
  2.   lanePriority: LanePriority, 
  3. ): ReactPriorityLevel { 
  4.   switch (lanePriority) { 
  5.     case SyncLanePriority: 
  6.     case SyncBatchedLanePriority: 
  7.       return ImmediateSchedulerPriority; 
  8.     case InputDiscreteHydrationLanePriority: 
  9.     case InputDiscreteLanePriority: 
  10.     case InputContinuousHydrationLanePriority: 
  11.     case InputContinuousLanePriority: 
  12.       return UserBlockingSchedulerPriority; 
  13.     case DefaultHydrationLanePriority: 
  14.     case DefaultLanePriority: 
  15.     case TransitionShortHydrationLanePriority: 
  16.     case TransitionShortLanePriority: 
  17.     case TransitionLongHydrationLanePriority: 
  18.     case TransitionLongLanePriority: 
  19.     case SelectiveHydrationLanePriority: 
  20.     case RetryLanePriority: 
  21.       return NormalSchedulerPriority; 
  22.     case IdleHydrationLanePriority: 
  23.     case IdleLanePriority:
  24.     case OffscreenLanePriority: 
  25.       return IdleSchedulerPriority; 
  26.     case NoLanePriority: 
  27.       return NoSchedulerPriority; 
  28.     default: 
  29.       invariant( 
  30.         false, 
  31.         'Invalid update priority: %s. This is a bug in React.', 
  32.         lanePriority,
  33.        ); 
  34.   } 
  35. }

總結(jié)

本文一共提到了4種優(yōu)先級:事件優(yōu)先級、更新優(yōu)先級、任務(wù)優(yōu)先級、調(diào)度優(yōu)先級,它們之間是遞進(jìn)的關(guān)系。事件優(yōu)先級由事件本身決定,更新優(yōu)先級由事件計(jì)算得出,然后放到root.pendingLanes,任務(wù)優(yōu)先級來自root.pendingLanes中最緊急的那些lanes對應(yīng)的優(yōu)先級,調(diào)度優(yōu)先級根據(jù)任務(wù)優(yōu)先級獲取。幾種優(yōu)先級環(huán)環(huán)相扣,保證了高優(yōu)任務(wù)的優(yōu)先執(zhí)行。


分享題目:帶你了解React中的優(yōu)先級
網(wǎng)站地址:http://www.dlmjj.cn/article/cojchho.html