干貨滿滿,React設計原理:藏在React源碼里的五指山(一)
最近在努力研究React源碼,發現它并沒有我之前想象的那么難理解。
雖然源碼里面有一些概念就像一座五指山困住了桀驁不馴的孫悟空。
但如果你理解了下面的幾個概念,讀懂react源碼就不是難事了。
?? 第一座山:Fiber相關變量命名
我們已經知道從v16.8開始,React進入了fiber架構時代,將不可中斷的遞歸改進為可中斷的遞歸。
fiber架構主要的工作是創建fiber tree,然后在合適的時機將這棵樹渲染在屏幕上.
所以圍繞著fiber,源碼里出現了一堆帶著fiber的變量。
?? FiberNode
首先,在源碼中,FiberNode是個構造函數,它包含了許多屬性。
function FiberNode(
this: $FlowFixMe,
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Instance
this.tag = tag;
this.key = key;
this.elementType = null;
this.type = null;
this.stateNode = null;
// Fiber
this.return = null;
this.child = null;
this.sibling = null;
this.index = 0;
this.ref = null;
this.refCleanup = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// Effects
this.flags = NoFlags;
this.subtreeFlags = NoFlags;
this.deletions = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null;
}
這些屬性可以根據FiberNode的不同身份進行劃分。
FiberNode在React中通常有三種不同的身份:
- ?? 作為架構的一環
作為架構的一環,多個FiberNode作為基本節點構成fiber tree。
此時,它的相關屬性如下:
// Fiber
// 指向父節點
this.return = null;
// 指向第一個子節點
this.child = null;
// 指向右邊兄弟節點
this.sibling = null;
this.index = 0;
- ?? 作為數據的一環
作為數據的一環,它保存了基本的React元素信息。
// Instance
// 對應組件的類型,可以是class、function等
this.tag = tag;
// 組件的key
this.key = key;
// 和type類似的屬性
this.elementType = null;
// 根據tag的不同,可以是calss、function、tagName(div、input等原始的標簽)
this.type = null;
// FiberNode對應的元素
this.stateNode = null;
這里說明一下React元素:
React元素可以是<div>Hello!</div>基本HTML元素,也可以是<App />這樣的組件,App是個類組件或者函數組件等。
- ?? 作為調度的一環
作為調度的一環,它提供了調度時的一些依據。
// render相關
this.flags = NoFlags;
this.subtreeFlags = NoFlags;
this.deletions = null;
// 優先級相關
this.lanes = NoLanes;
this.childLanes = NoLanes;
// 緩存相關
this.alternate = null;
?? fiberNode
前面說過,FiberNode是fiber tree最小單元。而React元素被編譯之后的VNode都成為FiberNode構造函數的實例,源碼中實例都用fiber或者workInProgress表示。
?? HostRootFiber
HostRootFiber是源碼里使用createHostRootFiber創建的Fiber根節點,它包含整棵組件樹的信息。對應的是如下代碼:
<body>
<div id="app"></div>
<div id="app2"></div>
<div id="app3"></div>
</body>
React允許你創建最多個HostRootFiber,也就是說,你可以有多個上述的掛載節點。
?? rootFiber
源碼里通過createHostRootFiber的實例在作為參數時,偶爾也會使用rootFiber表示。
?? FiberRootNode
FiberRootNode表示應用根節點。它保存著應用的狀態信息和組件信息。它的數據結構如下:
function FiberRootNode(
this: $FlowFixMe,
containerInfo: any,
// $FlowFixMe[missing-local-annot]
tag,
hydrate: any,
identifierPrefix: any,
onRecoverableError: any,
) {
this.tag = tag;
// 表示應用程序的容器元素,即組件樹的根節點
// 它一般是一個 DOM 元素,用來承載整個組件樹的渲染結果。
this.containerInfo = containerInfo;
// 表示當前應用程序中待處理的子樹列表
this.pendingChildren = null;
// 表示當前渲染的 Fiber 樹的根節點,指向 HootRootFiber
this.current = null;
// 網絡請求優化用的屬性
this.pingCache = null;
// 表示最近一次渲染完成的 Fiber 樹的根節點
// React 在進行組件更新時,會創建一個新的 Fiber 樹
// 并將它與舊的 Fiber 樹進行比較,找出需要更新的部分
// 然后進行更新。當更新完成后,最近一次渲染的結果
// 會存儲在 `finishedWork` 屬性中
this.finishedWork = null;
// 表示當前應用程序的上下文
this.context = null;
// 表示當前應用程序的掛起上下文
// 在 React 中,當組件的上下文發生變化時,
// React 會將新的上下文信息存儲在 `pendingContext` 中
// 待下一次更新時再進行處理。
this.pendingContext = null;
// 當組件完成更新后的回調函數
this.callbackNode = null;
// 表示下一次更新的過期時間
this.expirationTimes = createLaneMap(NoTimestamp);
// 優先級相關的屬性
this.pendingLanes = NoLanes;
this.suspendedLanes = NoLanes;
this.pingedLanes = NoLanes;
this.expiredLanes = NoLanes;
this.mutableReadLanes = NoLanes;
this.finishedLanes = NoLanes;
//....
}
通常狀況下,FiberRootNode和HootRootFiber是一一對應的關系。
FiberRootNode是單例對象,每個應用程序只會有一個實例,如果一個頁面有多個React應用,那么會有多個實例。
??fiberRootNode
fiberRootNode是createFiberRoot的返回值類型。即FiberRootNode實例。源碼里用fiberRoot表示。
?? 總結
在Fiber架構中,FiberNode實例fiber既是fiber tree的基本數據結構單元,記錄元素節點的信息,也是組件根節點的數據單元,記錄整個組件樹的信息,同時也會為調度相關的工作提供依據;
FiberRootNode的實例fiberRoot是應用根節點的數據單元,包含整個應用的狀態信息和租價信息。它和HootRootFiber實例rootFiber是一一對應關系。