日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

React狀態(tài)更新的優(yōu)先級機制源碼解析_React

作者:goClient1992 ? 更新時間: 2022-12-08 編程語言

為什么需要優(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) = 4

16的二進制值為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) == 16

1的二進制值為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í)行延遲任務的話,比如 SuspenduseTransitionuseDefferedValue 等特性。在 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)先級

這里會判斷 existingCallbackPrioritynewCallbackPriority 兩個優(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

欄目分類
最近更新