1. 程式人生 > 其它 >react原始碼解析7.Fiber架構

react原始碼解析7.Fiber架構

react原始碼解析7.Fiber架構

視訊講解(高效學習):點選學習

往期文章:

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

Fiber的深度理解

react15在render階段的reconcile是不可打斷的,這會在進行大量節點的reconcile時可能產生卡頓,因為瀏覽器所有的時間都交給了js執行,並且js的執行時單執行緒。為此react16之後就有了scheduler進行時間片的排程,給每個task(工作單元)一定的時間,如果在這個時間內沒執行完,也要交出執行權給瀏覽器進行繪製和重排,所以非同步可中斷的更新需要一定的資料結構在記憶體中來儲存工作單元的資訊,這個資料結構就是Fiber。

那麼有了Fiber這種資料結構後,能完成哪些事情呢,

  • 工作單元 任務分解 :Fiber最重要的功能就是作為工作單元,儲存原生節點或者元件節點對應資訊(包括優先順序),這些節點通過指標的形似形成Fiber樹
  • 增量渲染:通過jsx物件和current Fiber的對比,生成最小的差異補丁,應用到真實節點上
  • 根據優先順序暫停、繼續、排列優先順序:Fiber節點上儲存了優先順序,能通過不同節點優先順序的對比,達到任務的暫停、繼續、排列優先順序等能力,也為上層實現批量更新、Suspense提供了基礎
  • 儲存狀態:因為Fiber能儲存狀態和更新的資訊,所以就能實現函式元件的狀態更新,也就是hooks

Fiber的資料結構

Fiber的自帶的屬性如下:

//ReactFiber.old.js
function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  //作為靜態的資料結構 儲存節點的資訊 
  this.tag = tag;//對應元件的型別
  this.key = key;//key屬性
  this.elementType = null;//元素型別
  this.type = null;//func或者class
  this.stateNode = null;//真實dom節點

  //作為fiber數架構 連線成fiber樹
  this.return = null;//指向父節點
  this.child = null;//指向child
  this.sibling = null;//指向兄弟節點
  this.index = 0;

  this.ref = null;

  //用作為工作單元 來計算state
  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;
    
	//effect相關
  this.effectTag = NoEffect;
  this.nextEffect = null;
  this.firstEffect = null;
  this.lastEffect = null;

  //優先順序相關的屬性
  this.lanes = NoLanes;
  this.childLanes = NoLanes;

  //current和workInProgress的指標
  this.alternate = null;
}

Fiber雙快取

現在我們知道了Fiber可以儲存真實的dom,真實dom對應在記憶體中的Fiber節點會形成Fiber樹,這顆Fiber樹在react中叫current Fiber,也就是當前dom樹對應的Fiber樹,而正在構建Fiber樹叫workInProgress Fiber,這兩顆樹的節點通過alternate相連.

function App() {
  return (
		<>
      <h1>
        <p>count</p> xiaochen
      </h1>
    </>
  )
}

ReactDOM.render(<App />, document.getElementById("root"));

構建workInProgress Fiber發生在createWorkInProgress中,它能建立或者服用Fiber

//ReactFiber.old.js
export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
  let workInProgress = current.alternate;
  if (workInProgress === null) {//區分是在mount時還是在update時
    workInProgress = createFiber(
      current.tag,
      pendingProps,
      current.key,
      current.mode,
    );
    workInProgress.elementType = current.elementType;
    workInProgress.type = current.type;
    workInProgress.stateNode = current.stateNode;
   
    workInProgress.alternate = current;
    current.alternate = workInProgress;
  } else {
    workInProgress.pendingProps = pendingProps;//複用屬性
    workInProgress.type = current.type;
    workInProgress.flags = NoFlags;

    workInProgress.nextEffect = null;
    workInProgress.firstEffect = null;
    workInProgress.lastEffect = null;
	
    //...
  }

  workInProgress.childLanes = current.childLanes;//複用屬性
  workInProgress.lanes = current.lanes;

  workInProgress.child = current.child;
  workInProgress.memoizedProps = current.memoizedProps;
  workInProgress.memoizedState = current.memoizedState;
  workInProgress.updateQueue = current.updateQueue;

  const currentDependencies = current.dependencies;
  workInProgress.dependencies =
    currentDependencies === null
      ? null
      : {
          lanes: currentDependencies.lanes,
          firstContext: currentDependencies.firstContext,
        };

  workInProgress.sibling = current.sibling;
  workInProgress.index = current.index;
  workInProgress.ref = current.ref;


  return workInProgress;
}
  • 在mount時:會建立fiberRoot和rootFiber,然後根據jsx物件建立Fiber節點,節點連線成current Fiber樹。

  • 在update時:會根據新的狀態形成的jsx(ClassComponent的render或者FuncComponent的返回值)和current Fiber對比形(diff演算法)成一顆叫workInProgress的Fiber樹,然後將fiberRoot的current指向workInProgress樹,此時workInProgress就變成了current Fiber。fiberRoot:指整個應用的根節點,只存在一個

    fiberRoot:指整個應用的根節點,只存在一個

    rootFiber:ReactDOM.render或者ReactDOM.unstable_createRoot創建出來的應用的節點,可以存在多個。

我們現在知道了存在current Fiber和workInProgress Fiber兩顆Fiber樹,Fiber雙快取指的就是,在經過reconcile(diff)形成了新的workInProgress Fiber然後將workInProgress Fiber切換成current Fiber應用到真實dom中,存在雙Fiber的好處是在記憶體中形成檢視的描述,在最後應用到dom中,減少了對dom的操作。

現在來看看Fiber雙快取建立的過程圖

  • mount時:

    1. 剛開始只建立了fiberRoot和rootFiber兩個節點
    2. 然後根據jsx建立workInProgress Fiber:
    3. 把workInProgress Fiber切換成current Fiber
  • update時

    1. 根據current Fiber建立workInProgress Fiber
    2. 把workInProgress Fiber切換成current Fiber