React原始碼閱讀:概況

後排的風過發表於2018-07-19

前言

本文主要介紹一些React的設計思想和相關概念,不管是想要閱讀原始碼還是想深入瞭解React的同學看過來呀。歡迎指出錯誤,一起探討一起進步。

React閱讀系列文章

React原始碼閱讀:虛擬DOM的初始化

專案結構

React的相關程式碼都放在packages裡。

├── packages ------------------------------- React實現的相關程式碼
│   ├── create-subscription ------------------------- 在元件裡訂閱額外資料的工具
│   ├── events -------------------------- React事件相關
│   ├── react-art ------------------------- 畫圖相關庫
│   ├── react-dom -------------------------- ReactDom
│   ├── react-native-renderer ----------------------------- ReactNative
│   ├── react-reconciler ------------------------ React調製器
│   ├── react-scheduler ------------------------ 規劃React初始化,更新等等
│   ├── react-test-renderer ------------------------ 實驗性的React渲染器
│   ├── shared ------------------------ 公共程式碼
│   ├── simple-cache-provider ------------------------ 為React應用提供快取
複製程式碼

主要思想和設計原則

組合

React的主要特性就是各種組合而成的元件。由不同人編寫的元件可以組合使用,並且實現元件的複用。

時序安排(Scheduling)

React的初始化,更新,移除等等操作並不是同步的,使用者編寫的元件(如<ReactComponent/>)或者平臺特定元件(如<div/>)這些只是返回需要渲染的資訊,並不是真實DOM,我們通常稱他們為虛擬DOM,React會遍歷這顆虛擬DOM樹來渲染真實的DOM樹。

而且setState()也並不是同步的,他們的多次呼叫更新會被整合為一次更新,加入更新佇列然後等待更新,再去渲染真實的DOM樹,因為DOM型的操作通常是很耗時的,所以儘量減少DOM相關操作。

DOM之外

React的一個主要特點為編寫的程式碼可以通過工具(如ReactNative,Electron)在多個平臺上執行,因此渲染出來的元素可能不是DOM,因此React將他們分為兩個模組,一個模組為reconciler(調節器),它根據使用者編寫的元件轉換為React可以識別的元素(即虛擬DOM);另一個模組為renderer(渲染器,分為DOM渲染器和Native渲染器),它的作用為根據虛擬DOM渲染為平臺特定的元素。這樣的好處是可以方便React可以在多平臺上執行,而不必太過關注reconciler的程式碼。

相關概念

Fiber

什麼是Fiber?

Fiber是React版本16以後的重要概念。

Fiber的主要目標為:

  • 先暫停目前的工作去處理優先順序更高的工作,然後再回來繼續
  • 為不同的工作設定不同優先順序
  • 重用之前完成的工作
  • 終止不再需要的工作

我們都知道呼叫函式時會把它加入執行棧中,等函式執行完後再退出棧,那有沒有辦法可以定製棧的呼叫以更好地優化渲染UI呢?這就是Fiber想要解決的問題,我們可以稱單個Fiber為虛擬的棧幀,它是實現React時序安排的關鍵。

Fiber的結構

type Fiber = {|
  //  辨識元件相關的屬性
  tag: TypeOfWork,
  key: null | string,
  type: any,
  
  //  父Fiber或者null
  return: Fiber | null,

  child: Fiber | null,
  sibling: Fiber | null,
  index: number,
  ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject,

  memoizedProps: any, // The props used to create the output.
  updateQueue: UpdateQueue<any> | null,
  memoizedState: any,
  
  mode: TypeOfMode,

  effectTag: TypeOfSideEffect,
  nextEffect: Fiber | null,
  firstEffect: Fiber | null,
  lastEffect: Fiber | null,
  
  expirationTime: ExpirationTime,

  alternate: Fiber | null,

  //  時間
  actualDuration?: number,
  actualStartTime?: number,
  selfBaseTime?: number,
  treeBaseTime?: number,

 //  除錯
  _debugID?: number,
  _debugSource?: Source | null,
  _debugOwner?: Fiber | null,
  _debugIsCurrentlyTiming?: boolean,
|};
複製程式碼

keytype

這兩個屬性都是用來表示元件的,更新元件的時候先檢查key是否有改變,如果改變的話則直接摧毀重建;不變的話則繼續檢查type是否改變,改變則直接摧毀重建;否則重用原來的元件,只是改變元件的屬性。type可能是使用者定義的函式型或者型別的組合型元件,或者是代表平臺元素的字串(如'div')。

pendingPropsmemoizedProps

概念上來說,props就是一個函式的引數陣列。pendingProps是Fiber開始執行的時候被設定的,memoizedProps在執行完之後。每次更新的時候先比較pendingPropsmemoizedProps,如果它們相同,則表示先前的輸出可以重用,而不必進行多餘的工作。

pendingWorkPriority

一個表面工作優先權的屬性。除NoWork(它的值為0)以外,數值越大代表有限權越低。

function matchesPriority(fiber, priority) {
  return fiber.pendingWorkPriority !== 0 &&
         fiber.pendingWorkPriority <= priority
}
複製程式碼

參考文獻

相關文章