React Fiber 任務(wù)分片 & 時間分片

源碼基于react@16.13.1

Fiber 是一個工作單元,它的引入是react實現(xiàn)任務(wù)分片和時間分片的基礎(chǔ)。分片是為了在Reconciliation階段(純js計算,無DOM操作)一點一點地執(zhí)行任務(wù),給瀏覽器喘息的機(jī)會,從而在體驗上給用戶更流暢的使用感受。

任務(wù)分片

一個工作單元是什么?可以從代碼中直觀地理解。

import React from 'react'
import ReactDOM from 'react-dom'

function App() {
    return (
        <div>
            <h1>Title</h1>
            <p>
                <a href='#'>link</a>
            </p>
        </div>
    )
}

ReactDOM.createRoot(document.getElementById('app')).render(<App />)

以上結(jié)構(gòu),被react分解成了6個fiber,也就是6個工作單元,如下圖

react fiber

其中null對應(yīng)著fiber的根節(jié)點,雖然在視覺上是什么都看不見的,但它的確在內(nèi)存里。剩下的fiber節(jié)點都比較好理解。

每當(dāng)更新發(fā)生時,react會從fiber的根節(jié)點開始,一個一個地循環(huán)遍歷所有的fiber。

react 通過循環(huán),一個一個地對fiber執(zhí)行performUnitOfWork操作,以此實現(xiàn)了任務(wù)分片。

時間分片

時間分片的邏輯藏在循環(huán)里。

//react-reconciler -> ReactFiberWorkLoop.js
function workLoopConcurrent() {
  // Perform work until Scheduler asks us to yield
  while (workInProgress !== null && !shouldYield()) {
    workInProgress = performUnitOfWork(workInProgress);
  }
}

performUnitOfWork可能返回null或者下一個需要被執(zhí)行的fiber,返回結(jié)果存在workInProgress中。workInProgress在react-reconciler模塊中是全局變量。

當(dāng)shouldYield返回true的時候,循環(huán)語句中斷,一個時間分片就結(jié)束了,瀏覽器將重獲控制權(quán)。

以下任意條件成立時,shouldYield會返回true

  • 時間片到期(默認(rèn)5ms)
  • 更緊急任務(wù)插隊

react 通過中斷任務(wù)循環(huán),實現(xiàn)了時間分片。

任務(wù)恢復(fù)

循環(huán)中斷時,下一個未被完成的任務(wù)已經(jīng)被保存到react-reconciler模塊的全局變量workInProgress中。下一次循環(huán)開始時就從workInProgress開始。

跳出循環(huán)之后,react還做了一件事,通過MessageChannel發(fā)起了一個postMessage事件。

以上都發(fā)生在瀏覽器重獲控制權(quán)之前。

而監(jiān)聽這個事件的,正是循環(huán)的發(fā)起者performWorkUntilDeadline。

// scheduler.development.js
const performWorkUntilDeadline = () => {
    if (scheduledHostCallback !== null) {
      const currentTime = getCurrentTime();
      deadline = currentTime + yieldInterval;
      const hasTimeRemaining = true;
      try {
        // 通過scheduledHostCallback發(fā)起workLoopConcurrent循環(huán)
        const hasMoreWork = scheduledHostCallback(
          hasTimeRemaining,
          currentTime,
        );
        if (!hasMoreWork) {
          isMessageLoopRunning = false;
          scheduledHostCallback = null;
        } else {
          port.postMessage(null); // 發(fā)起postMessage事件
        }
      } catch (error) {
        port.postMessage(null);
        throw error;
      }
    } else {
      isMessageLoopRunning = false;
    }
    needsPaint = false;
  };

  const channel = new MessageChannel();
  const port = channel.port2;
  channel.port1.onmessage = performWorkUntilDeadline;

循環(huán)中斷之后react執(zhí)行 port.postMessage 發(fā)起了一個message事件,并且通過事件監(jiān)聽又恢復(fù)了循環(huán)。在循環(huán)中斷到事件響應(yīng)的間隙,瀏覽器重獲了控制權(quán),執(zhí)行必要的渲染工作(如果有的話)。

以上特性,目前只在開啟concurrent模式時有效。默認(rèn)模式下只有任務(wù)分片,但是是同步執(zhí)行的,所以不存在時間分片。


迅速搭建React源碼測試環(huán)境

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容