react源碼解析6.legacy模式和concurrent模式


react源碼解析6.legacy模式和concurrent模式

視頻課程(高效學習):進入課程

課程目錄:

1.開篇介紹和面試題

2.react的設計理念

3.react源碼架構

4.源碼目錄結構和調試

5.jsx&核心api

6.legacy和concurrent模式入口函數

7.Fiber架構

8.render階段

9.diff算法

10.commit階段

11.生命周期

12.狀態更新流程

13.hooks源碼

14.手寫hooks

15.scheduler&Lane

16.concurrent模式

17.context

18事件系統

19.手寫迷你版react

20.總結&第一章的面試題解答

21.demo

react啟動的模式

react有3種模式進入主體函數的入口,我們可以從 react官方文檔 使用 Concurrent 模式(實驗性)中對比三種模式:

  • legacy 模式: ReactDOM.render(<App />, rootNode)。這是當前 React app 使用的方式。當前沒有計划刪除本模式,但是這個模式可能不支持這些新功能。
  • blocking 模式: ReactDOM.createBlockingRoot(rootNode).render(<App />)。目前正在實驗中。作為遷移到 concurrent 模式的第一個步驟。
  • concurrent 模式: ReactDOM.createRoot(rootNode).render(<App />)。目前在實驗中,未來穩定之后,打算作為 React 的默認開發模式。這個模式開啟了所有的新功能。

特性對比:

react源碼6.1

legacy 模式在合成事件中有自動批處理的功能,但僅限於一個瀏覽器任務。非 React 事件想使用這個功能必須使用 unstable_batchedUpdates。在 blocking 模式和 concurrent 模式下,所有的 setState 在默認情況下都是批處理的。會在開發中發出警告

不同模式在react運行時的含義

legacy模式是我們常用的,它構建dom的過程是同步的,所以在render的reconciler中,如果diff的過程特別耗時,那么導致的結果就是js一直阻塞高優先級的任務(例如用戶的點擊事件),表現為頁面的卡頓,無法響應。

concurrent Mode是react未來的模式,它用時間片調度實現了異步可中斷的任務,根據設備性能的不同,時間片的長度也不一樣,在每個時間片中,如果任務到了過期時間,就會主動讓出線程給高優先級的任務。這部分將在第15節 scheduler&lane模型 。

兩種模式函數主要執行過程

1.主要執行流程:

react源碼6.3

2.詳細函數調用過程

用demo_0跟着視頻調試更加清晰,黃色部分是主要任務是創建fiberRootNode和rootFiber,紅色部分是創建Update,藍色部分是調度render階段的入口函數

react源碼6.2

3.legacy模式:

  • render調用legacyRenderSubtreeIntoContainer,最后createRootImpl會調用到createFiberRoot創建fiberRootNode,然后調用createHostRootFiber創建rootFiber,其中fiberRootNode是整個項目的的根節點,rootFiber是當前應用掛在的節點,也就是ReactDOM.render調用后的根節點

    //最上層的節點是整個項目的根節點fiberRootNode
    ReactDOM.render(<App />, document.getElementById("root"));//rootFiber
    ReactDOM.render(<App />, document.getElementById("root"));//rootFiber
    

    react源碼7.1

  • 創建完Fiber節點后,legacyRenderSubtreeIntoContainer調用updateContainer創建創建Update對象掛載到updateQueue的環形鏈表上,然后執行scheduleUpdateOnFiber調用performSyncWorkOnRoot進入render階段和commit階段

4.concurrent模式:

  • createRoot調用createRootImpl創建fiberRootNode和rootNode
  • 創建完Fiber節點后,調用ReactDOMRoot.prototype.render執行updateContainer,然后scheduleUpdateOnFiber異步調度performConcurrentWorkOnRoot進入render階段和commit階段

5.legacy模式主要函數注釋

function legacyRenderSubtreeIntoContainer(parentComponent, children, container, forceHydrate, callback) {
  //...
  var root = container._reactRootContainer;
  var fiberRoot;

  if (!root) {
    // mount時
    root = container._reactRootContainer = legacyCreateRootFromDOMContainer(container, forceHydrate);//創建root節點
    fiberRoot = root._internalRoot;

    if (typeof callback === 'function') {//處理回調
      var originalCallback = callback;

      callback = function () {
        var instance = getPublicRootInstance(fiberRoot);
        originalCallback.call(instance);
      };
    } 


    unbatchedUpdates(function () {
      updateContainer(children, fiberRoot, parentComponent, callback);//創建update入口
    });
  } else {
    // update時
    fiberRoot = root._internalRoot;

    if (typeof callback === 'function') {//處理回調
      var _originalCallback = callback;

      callback = function () {
        var instance = getPublicRootInstance(fiberRoot);

        _originalCallback.call(instance);
      };
    } 
    
    updateContainer(children, fiberRoot, parentComponent, callback);
  }
}
function createFiberRoot(containerInfo, tag, hydrate, hydrationCallbacks) {
  var root = new FiberRootNode(containerInfo, tag, hydrate);//創建fiberRootNode
  const uninitializedFiber = createHostRootFiber(tag);//創建rootFiber
  //rootFiber和fiberRootNode連接
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;
  //創建updateQueue
  initializeUpdateQueue(uninitializedFiber);
  return root;
}

//對於HostRoot或者ClassComponent會使用initializeUpdateQueue創建updateQueue,然后將updateQueue掛載到fiber節點上
export function initializeUpdateQueue<State>(fiber: Fiber): void {
  const queue: UpdateQueue<State> = {
    baseState: fiber.memoizedState,//初始state,后面會基於這個state,根據Update計算新的state
    firstBaseUpdate: null,//Update形成的鏈表的頭
    lastBaseUpdate: null,//Update形成的鏈表的尾
		//新產生的update會以單向環狀鏈表保存在shared.pending上,計算state的時候會剪開這個環狀鏈表,並且連接在			  //lastBaseUpdate后
    shared: {
      pending: null,
    },
    effects: null,
  };
  fiber.updateQueue = queue;
}
function updateContainer(element, container, parentComponent, callback) {
  var lane = requestUpdateLane(current$1);//獲取當前可用lane 在12章講解
  var update = createUpdate(eventTime, lane); //創建update

  update.payload = {
    element: element//jsx
  };

  enqueueUpdate(current$1, update);//update入隊
  scheduleUpdateOnFiber(current$1, lane, eventTime);//調度update
  return lane;
}
function scheduleUpdateOnFiber(fiber, lane, eventTime) {
  if (lane === SyncLane) {//同步lane 對應legacy模式
    //...
    performSyncWorkOnRoot(root);//render階段的起點 render在第6章講解
  } else {//concurrent模式
    //...
    ensureRootIsScheduled(root, eventTime);//確保root被調度
  } 
}

6.concurrent主要函數注釋:

function ensureRootIsScheduled(root, currentTime) {
  //...
  var nextLanes = getNextLanes(root, root === workInProgressRoot ? workInProgressRootRenderLanes : NoLanes); //計算nextLanes

  //...

 //將lane的優先級轉換成schduler的優先級
  var schedulerPriorityLevel = lanePriorityToSchedulerPriority(newCallbackPriority);
  //以schedulerPriorityLevel的優先級執行performConcurrentWorkOnRoot 也就是concurrent模式的起點
  newCallbackNode = 	  scheduleCallback(schedulerPriorityLevel,performConcurrentWorkOnRoot.bind(null, root));
}

7.兩種模式的不同點:

  1. createRootImpl中傳入的第二個參數不一樣 一個是LegacyRoot一個是ConcurrentRoot
  2. requestUpdateLane中獲取的lane的優先級不同
  3. 在函數scheduleUpdateOnFiber中根據不同優先級進入不同分支,legacy模式進入performSyncWorkOnRoot,concurrent模式會異步調度performConcurrentWorkOnRoot


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM