<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          React中的4種優(yōu)先級(jí)

          共 9359字,需瀏覽 19分鐘

           ·

          2021-01-23 20:42

          來源 |?http://www.fly63.com/article/detial/10072
          UI產(chǎn)生交互的根本原因是各種事件,這也就意味著事件與更新有著直接關(guān)系。不同事件產(chǎn)生的更新,它們的優(yōu)先級(jí)是有差異的,所以更新優(yōu)先級(jí)的根源在于事件的優(yōu)先級(jí)。一個(gè)更新的產(chǎn)生可直接導(dǎo)致react生成一個(gè)更新任務(wù),最終這個(gè)任務(wù)被Scheduler調(diào)度。
          所以在React中,人為地將事件劃分了等級(jí),最終目的是決定調(diào)度任務(wù)的輕重緩急,因此,React有一套從事件到調(diào)度的優(yōu)先級(jí)機(jī)制。
          本文將圍繞事件優(yōu)先級(jí)、更新優(yōu)先級(jí)、任務(wù)優(yōu)先級(jí)、調(diào)度優(yōu)先級(jí),重點(diǎn)梳理它們之間的轉(zhuǎn)化關(guān)系。
          • 事件優(yōu)先級(jí):按照用戶事件的交互緊急程度,劃分的優(yōu)先級(jí)
          • 更新優(yōu)先級(jí):事件導(dǎo)致React產(chǎn)生的更新對(duì)象(update)的優(yōu)先級(jí)(update.lane)
          • 任務(wù)優(yōu)先級(jí):產(chǎn)生更新對(duì)象之后,React去執(zhí)行一個(gè)更新任務(wù),這個(gè)任務(wù)所持有的優(yōu)先級(jí)
          • 調(diào)度優(yōu)先級(jí):Scheduler依據(jù)React更新任務(wù)生成一個(gè)調(diào)度任務(wù),這個(gè)調(diào)度任務(wù)所持有的優(yōu)先級(jí)
          前三者屬于React的優(yōu)先級(jí)機(jī)制,第四個(gè)屬于Scheduler的優(yōu)先級(jí)機(jī)制,Scheduler內(nèi)部有自己的優(yōu)先級(jí)機(jī)制,雖然與React有所區(qū)別,但等級(jí)的劃分基本一致。下面我們從事件優(yōu)先級(jí)開始說起。

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

          React按照事件的緊急程度,把它們劃分成三個(gè)等級(jí):
          • 離散事件(DiscreteEvent):click、keydown、focusin等,這些事件的觸發(fā)不是連續(xù)的,優(yōu)先級(jí)為0。
          • 用戶阻塞事件(UserBlockingEvent):drag、scroll、mouseover等,特點(diǎn)是連續(xù)觸發(fā),阻塞渲染,優(yōu)先級(jí)為1。
          • 連續(xù)事件(ContinuousEvent):canplay、error、audio標(biāo)簽的timeupdate和canplay,優(yōu)先級(jí)最高,為2。

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

          事件優(yōu)先級(jí)是在注冊(cè)階段被確定的,在向root上注冊(cè)事件時(shí),會(huì)根據(jù)事件的類別,創(chuàng)建不同優(yōu)先級(jí)的事件監(jiān)聽(listener),最終將它綁定到root上去。
          let listener = createEventListenerWrapperWithPriority(    targetContainer,    domEventName,    eventSystemFlags,    listenerPriority,  );
          createEventListenerWrapperWithPriority函數(shù)的名字已經(jīng)把它做的事情交代得八九不離十了。它會(huì)首先根據(jù)事件的名稱去找對(duì)應(yīng)的事件優(yōu)先級(jí),然后依據(jù)優(yōu)先級(jí)返回不同的事件監(jiān)聽函數(shù)。
          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上的事件監(jiān)聽其實(shí)是dispatchDiscreteEvent、dispatchUserBlockingUpdate、dispatchEvent這三個(gè)中的一個(gè)。它們做的事情都是一樣的,以各自的事件優(yōu)先級(jí)去執(zhí)行真正的事件處理函數(shù)。
          比如:dispatchDiscreteEvent和dispatchUserBlockingUpdate最終都會(huì)以UserBlockingEvent的事件級(jí)別去執(zhí)行事件處理函數(shù)。
          以某種優(yōu)先級(jí)去執(zhí)行事件處理函數(shù)其實(shí)要借助Scheduler中提供的runWithPriority函數(shù)來實(shí)現(xiàn):
          function dispatchUserBlockingUpdate(  domEventName,  eventSystemFlags,  container,  nativeEvent,) {
          ...
          runWithPriority( UserBlockingPriority, dispatchEvent.bind( null, domEventName, eventSystemFlags, container, nativeEvent, ), );
          ...
          }
          這么做可以將事件優(yōu)先級(jí)記錄到Scheduler中,相當(dāng)于告訴Scheduler:你幫我記錄一下當(dāng)前事件派發(fā)的優(yōu)先級(jí),等React那邊創(chuàng)建更新對(duì)象(即update)計(jì)算更新優(yōu)先級(jí)時(shí)直接從你這拿就好了。
          function unstable_runWithPriority(priorityLevel, eventHandler) {  switch (priorityLevel) {    case ImmediatePriority:    case UserBlockingPriority:    case NormalPriority:    case LowPriority:    case IdlePriority:      break;    default:      priorityLevel = NormalPriority;  }
          var previousPriorityLevel = currentPriorityLevel; // 記錄優(yōu)先級(jí)到Scheduler內(nèi)部的變量里 currentPriorityLevel = priorityLevel;
          try { return eventHandler(); } finally { currentPriorityLevel = previousPriorityLevel; }}

          更新優(yōu)先級(jí)

          以setState為例,事件的執(zhí)行會(huì)導(dǎo)致setState執(zhí)行,而setState本質(zhì)上是調(diào)用enqueueSetState,生成一個(gè)update對(duì)象,這時(shí)候會(huì)計(jì)算它的更新優(yōu)先級(jí),即update.lane:
          const classComponentUpdater = {  enqueueSetState(inst, payload, callback) {    ...
          // 依據(jù)事件優(yōu)先級(jí)創(chuàng)建update的優(yōu)先級(jí) const lane = requestUpdateLane(fiber, suspenseConfig);
          const update = createUpdate(eventTime, lane, suspenseConfig); update.payload = payload; enqueueUpdate(fiber, update);
          // 開始調(diào)度 scheduleUpdateOnFiber(fiber, lane, eventTime); ... },};
          重點(diǎn)關(guān)注requestUpdateLane,它首先找出Scheduler中記錄的優(yōu)先級(jí):schedulerPriority,然后計(jì)算更新優(yōu)先級(jí):lane,具體的計(jì)算過程在findUpdateLane函數(shù)中,計(jì)算過程是一個(gè)從高到低依次占用空閑位的操作,具體的代碼在這里?,這里就先不詳細(xì)展開。
          export function requestUpdateLane(  fiber: Fiber,  suspenseConfig: SuspenseConfig | null,): Lane {
          ... // 根據(jù)記錄下的事件優(yōu)先級(jí),獲取任務(wù)調(diào)度優(yōu)先級(jí) const schedulerPriority = getCurrentPriorityLevel();
          let lane; if ( (executionContext & DiscreteEventContext) !== NoContext && schedulerPriority === UserBlockingSchedulerPriority ) { // 如果事件優(yōu)先級(jí)是用戶阻塞級(jí)別,則直接用InputDiscreteLanePriority去計(jì)算更新優(yōu)先級(jí) lane = findUpdateLane(InputDiscreteLanePriority, currentEventWipLanes); } else { // 依據(jù)事件的優(yōu)先級(jí)去計(jì)算schedulerLanePriority const schedulerLanePriority = schedulerPriorityToLanePriority( schedulerPriority, ); ... // 根據(jù)事件優(yōu)先級(jí)計(jì)算得來的schedulerLanePriority,去計(jì)算更新優(yōu)先級(jí) lane = findUpdateLane(schedulerLanePriority, currentEventWipLanes); } return lane;}getCurrentPriorityLevel負(fù)責(zé)讀取記錄在Scheduler中的優(yōu)先級(jí):function unstable_getCurrentPriorityLevel() { return currentPriorityLevel;}
          update對(duì)象創(chuàng)建完成后意味著需要對(duì)頁(yè)面進(jìn)行更新,會(huì)調(diào)用scheduleUpdateOnFiber進(jìn)入調(diào)度,而真正開始調(diào)度之前會(huì)計(jì)算本次產(chǎn)生的更新任務(wù)的任務(wù)優(yōu)先級(jí),目的是與已有任務(wù)的任務(wù)優(yōu)先級(jí)去做比較,便于做出多任務(wù)的調(diào)度決策。
          調(diào)度決策的邏輯在ensureRootIsScheduled 函數(shù)中,這是一個(gè)非常重要的函數(shù),控制著React任務(wù)進(jìn)入Scheduler的大門。

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

          一個(gè)update會(huì)被一個(gè)React的更新任務(wù)執(zhí)行掉,任務(wù)優(yōu)先級(jí)被用來區(qū)分多個(gè)更新任務(wù)的緊急程度,它由更新優(yōu)先級(jí)計(jì)算而來,舉例來說:
          假設(shè)產(chǎn)生一前一后兩個(gè)update,它們持有各自的更新優(yōu)先級(jí),也會(huì)被各自的更新任務(wù)執(zhí)行。經(jīng)過優(yōu)先級(jí)計(jì)算,如果后者的任務(wù)優(yōu)先級(jí)高于前者的任務(wù)優(yōu)先級(jí),那么會(huì)讓Scheduler取消前者的任務(wù)調(diào)度;如果后者的任務(wù)優(yōu)先級(jí)等于前者的任務(wù)優(yōu)先級(jí),后者不會(huì)導(dǎo)致前者被取消,而是會(huì)復(fù)用前者的更新任務(wù),將兩個(gè)同等優(yōu)先級(jí)的更新收斂到一次任務(wù)中;如果后者的任務(wù)優(yōu)先級(jí)低于前者的任務(wù)優(yōu)先級(jí),同樣不會(huì)導(dǎo)致前者的任務(wù)被取消,而是在前者更新完成后,再次用Scheduler對(duì)后者發(fā)起一次任務(wù)調(diào)度。
          這是任務(wù)優(yōu)先級(jí)存在的意義,保證高優(yōu)先級(jí)任務(wù)及時(shí)響應(yīng),收斂同等優(yōu)先級(jí)的任務(wù)調(diào)度。
          任務(wù)優(yōu)先級(jí)在即將調(diào)度的時(shí)候去計(jì)算,代碼在ensureRootIsScheduled函數(shù)中:
          function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
          ...
          // 獲取nextLanes,順便計(jì)算任務(wù)優(yōu)先級(jí) const nextLanes = getNextLanes( root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes, );
          // 獲取上面計(jì)算得出的任務(wù)優(yōu)先級(jí) const newCallbackPriority = returnNextLanesPriority();
          ...
          }
          通過調(diào)用getNextLanes去計(jì)算在本次更新中應(yīng)該處理的這批lanes(nextLanes),getNextLanes會(huì)調(diào)用getHighestPriorityLanes去計(jì)算任務(wù)優(yōu)先級(jí)。任務(wù)優(yōu)先級(jí)計(jì)算的原理是這樣:更新優(yōu)先級(jí)(update的lane),
          它會(huì)被并入root.pendingLanes,root.pendingLanes經(jīng)過getNextLanes處理后,挑出那些應(yīng)該處理的lanes,傳入getHighestPriorityLanes,根據(jù)nextLanes找出這些lanes的優(yōu)先級(jí)作為任務(wù)優(yōu)先級(jí)。
          function getHighestPriorityLanes(lanes: Lanes | Lane): Lanes {  ...  // 都是這種比較賦值的過程,這里只保留兩個(gè)以做簡(jiǎn)要說明  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就是任務(wù)優(yōu)先級(jí),它有如下這些值,值越大,優(yōu)先級(jí)越高,暫時(shí)只理解任務(wù)優(yōu)先級(jí)的作用即可。
          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;
          如果已經(jīng)存在一個(gè)更新任務(wù),ensureRootIsScheduled會(huì)在獲取到新任務(wù)的任務(wù)優(yōu)先級(jí)之后,去和舊任務(wù)的任務(wù)優(yōu)先級(jí)去比較,從而做出是否需要重新發(fā)起調(diào)度的決定,若需要發(fā)起調(diào)度,那么會(huì)去計(jì)算調(diào)度優(yōu)先級(jí)。

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

          一旦任務(wù)被調(diào)度,那么它就會(huì)進(jìn)入Scheduler,在Scheduler中,這個(gè)任務(wù)會(huì)被包裝一下,生成一個(gè)屬于Scheduler自己的task,這個(gè)task持有的優(yōu)先級(jí)就是調(diào)度優(yōu)先級(jí)。
          它有什么作用呢?在Scheduler中,分別用過期任務(wù)隊(duì)列和未過期任務(wù)的隊(duì)列去管理它內(nèi)部的task,過期任務(wù)的隊(duì)列中的task根據(jù)過期時(shí)間去排序,最早過期的排在前面,便于被最先處理。而過期時(shí)間是由調(diào)度優(yōu)先級(jí)計(jì)算的出的,不同的調(diào)度優(yōu)先級(jí)對(duì)應(yīng)的過期時(shí)間不同。
          調(diào)度優(yōu)先級(jí)由任務(wù)優(yōu)先級(jí)計(jì)算得出,在ensureRootIsScheduled更新真正讓Scheduler發(fā)起調(diào)度的時(shí)候,會(huì)去計(jì)算調(diào)度優(yōu)先級(jí)。
          function ensureRootIsScheduled(root: FiberRoot, currentTime: number) {
          ...
          // 根據(jù)任務(wù)優(yōu)先級(jí)獲取Scheduler的調(diào)度優(yōu)先級(jí) const schedulerPriorityLevel = lanePriorityToSchedulerPriority( newCallbackPriority, );
          // 計(jì)算出調(diào)度優(yōu)先級(jí)之后,開始讓Scheduler調(diào)度React的更新任務(wù) newCallbackNode = scheduleCallback( schedulerPriorityLevel, performConcurrentWorkOnRoot.bind(null, root), );
          ...}
          lanePriorityToSchedulerPriority計(jì)算調(diào)度優(yōu)先級(jí)的過程是根據(jù)任務(wù)優(yōu)先級(jí)找出對(duì)應(yīng)的調(diào)度優(yōu)先級(jí)。
          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,      );  }}

          總結(jié)

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

          本文完?

          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产最新自拍 | 国产日韩欧美在线影院 | 天天日天天摸天天操 | 亚洲日韩第一页 | 北条麻妃无码在线观看 |