欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React?Fiber原理深入分析

 更新時間:2023年01月10日 16:32:33   作者:goClient1992  
Fiber可以理解為一個執(zhí)行單元,每次執(zhí)行完一個執(zhí)行單元,React?Fiber就會檢查還剩多少時間,如果沒有時間則將控制權讓出去,然后由瀏覽器執(zhí)行渲染操作,這篇文章主要介紹了React?Fiber架構原理剖析,需要的朋友可以參考下<BR>

react16 版本之后引入了 fiber,整個架構層面的 調度、協調、diff 算法以及渲染等都與 fiber 密切相關。所以為了更好地講解后面的內容,需要對 fiber 有個比較清晰的認知。本章將介紹以下內容:

為什么需要 fiberfiber 節(jié)點結構中的屬性fiber 樹是如何構建與更新的

為什么需要 fiber

Lin Clark 在 React Conf 2017 的演講中,他通過漫畫的形式,很好地講述了 fiber 為何出現,下面我根據她的演講,結合我自己的理解來談一談 fiber 出現的原因。

fiber 之前

在 react15 及之前 fiber 未出現時,react 的一系列執(zhí)行過程例如生命周期執(zhí)行、虛擬 dom 的比較、dom 樹的更新等都是同步的,一旦開始執(zhí)行就不會中斷,直到所有的工作流程全部結束為止。

要知道,react 所有的狀態(tài)更新,都是從根組件開始的,當應用組件樹比較龐大時,一旦狀態(tài)開始變更,組件樹層層遞歸開始更新,js 主線程就不得不停止其他工作。例如組件樹一共有 1000 個組件需要更新,每個組件更新所需要的時間為 1s,那么在這 1s 內瀏覽器都無法做其他的事情,用戶的點擊輸入等交互事件、頁面動畫等都不會得到響應,體驗就會非常的差。

這種情況下,函數堆棧的調用就像下圖一樣,層級很深,很長時間不會返回

fiber 之后

為了解決這一問題,react 引入了 fiber 這種數據結構,將更新渲染耗時長的大任務,分為許多的小片。每個小片的任務執(zhí)行完成后,都先去執(zhí)行其他高優(yōu)先級的任務(例如用戶點擊輸入事件、動畫等),這樣 js 的主線程就不會被 react 獨占,雖然任務執(zhí)行的總時間不變,但是頁面能夠及時響應高優(yōu)先級任務,顯得不會卡頓了。

fiber 分片模式下,瀏覽器主線程能夠定期被釋放,保證了渲染的幀率,函數的堆棧調用如下(波谷表示執(zhí)行分片任務,波峰表示執(zhí)行其他高優(yōu)先級任務):

react 通過 fiber,為我們提供了一種跟蹤、調度、暫停和中止工作的便捷方式,保證了頁面的性能和流暢度。

fiber 節(jié)點結構

fiber 是一種數據結構,每個 fiber 節(jié)點的內部,都保存了 dom 相關信息、fiber 樹相關的引用、要更新時的副作用等,我們可以看一下源碼中的 fiber 結構:

// packages/react-reconciler/src/ReactInternalTypes.js
export type Fiber = {|
  // 作為靜態(tài)數據結構,存儲節(jié)點 dom 相關信息
  tag: WorkTag, // 組件的類型,取決于 react 的元素類型
  key: null | string,
  elementType: any, // 元素類型
  type: any, // 定義與此fiber關聯的功能或類。對于組件,它指向構造函數;對于DOM元素,它指定HTML tag
  stateNode: any, // 真實 dom 節(jié)點
  // fiber 鏈表樹相關
  return: Fiber | null, // 父 fiber
  child: Fiber | null, // 第一個子 fiber
  sibling: Fiber | null, // 下一個兄弟 fiber
  index: number, // 在父 fiber 下面的子 fiber 中的下標
  ref:
    | null
    | (((handle: mixed) => void) & {_stringRef: ?string, ...})
    | RefObject,
  // 工作單元,用于計算 state 和 props 渲染
  pendingProps: any, // 本次渲染需要使用的 props
  memoizedProps: any, // 上次渲染使用的 props
  updateQueue: mixed, // 用于狀態(tài)更新、回調函數、DOM更新的隊列
  memoizedState: any, // 上次渲染后的 state 狀態(tài)
  dependencies: Dependencies | null, // contexts、events 等依賴
  mode: TypeOfMode,
  // 副作用相關
  flags: Flags, // 記錄更新時當前 fiber 的副作用(刪除、更新、替換等)狀態(tài)
  subtreeFlags: Flags, // 當前子樹的副作用狀態(tài)
  deletions: Array<Fiber> | null, // 要刪除的子 fiber
  nextEffect: Fiber | null, // 下一個有副作用的 fiber
  firstEffect: Fiber | null, // 指向第一個有副作用的 fiber
  lastEffect: Fiber | null, // 指向最后一個有副作用的 fiber 
  // 優(yōu)先級相關
  lanes: Lanes,
  childLanes: Lanes,
  alternate: Fiber | null, // 指向 workInProgress fiber 樹中對應的節(jié)點
  actualDuration?: number,
  actualStartTime?: number,
  selfBaseDuration?: number,
  treeBaseDuration?: number,
  _debugID?: number,
  _debugSource?: Source | null,
  _debugOwner?: Fiber | null,
  _debugIsCurrentlyTiming?: boolean,
  _debugNeedsRemount?: boolean,
  _debugHookTypes?: Array<HookType> | null,
|};

dom 相關屬性

fiber 中和 dom 節(jié)點相關的信息主要關注 tag、keytypestateNode。

tag

fiber 中 tag 屬性的 ts 類型為 workType,用于標記不同的 react 組件類型,我們可以看一下源碼中 workType 的枚舉值:

// packages/react-reconciler/src/ReactWorkTags.js
export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
export const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
export const HostComponent = 5;
export const HostText = 6;
export const Fragment = 7;
export const Mode = 8;
export const ContextConsumer = 9;
export const ContextProvider = 10;
export const ForwardRef = 11;
export const Profiler = 12;
export const SuspenseComponent = 13;
export const MemoComponent = 14;
export const SimpleMemoComponent = 15;
export const LazyComponent = 16;
export const IncompleteClassComponent = 17;
export const DehydratedFragment = 18;
export const SuspenseListComponent = 19;
export const FundamentalComponent = 20;
export const ScopeComponent = 21;
export const Block = 22;
export const OffscreenComponent = 23;
export const LegacyHiddenComponent = 24;

在 react 協調時,beginWork 和 completeWork 等流程時,都會根據 tag 類型的不同,去執(zhí)行不同的函數處理 fiber 節(jié)點。

key 和 type

keytype 兩項用于 react diff 過程中確定 fiber 是否可以復用。

key 為用戶定義的唯一值。type 定義與此fiber關聯的功能或類。對于組件,它指向函數或者類本身;對于DOM元素,它指定HTML tag。

stateNode

stateNode用于記錄當前fiber所對應的真實dom節(jié)點或者當前虛擬組件的實例,這么做的原因第一是為了實現Ref,第二是為了實現真實 dom的跟蹤。

鏈表樹相關屬性

我們看一下和 fiber 鏈表樹構建相關的 return、childsibling 幾個字段:

  • return:指向父 fiber,若沒有父 fiber 則為 null
  • child: 指向第一個子 fiber,若沒有任何子 fiber 則為 null
  • sibling:指向下一個兄弟 fiber,若沒有下一個兄弟 fiber 則為 null

通過這幾個字段,各個 fiber 節(jié)點構成了 fiber 鏈表樹結構:

副作用相關屬性

首先理解一下 react 中的副作用,舉一個生活中比較通俗的例子:我們感冒了本來吃點藥就沒事了,但是吃了藥發(fā)現身體過敏了,而這個“過敏”就是副作用。react 中,我們修改了 state、props、ref 等數據,除了數據改變之外,還會引起 dom 的變化,這種 render 階段不能完成的工作,我們稱之為副作用。相關參考講解:進入學習

flags

react 中通過 flags 記錄每個節(jié)點diff后需要變更的狀態(tài),例如 dom 的添加、替換、刪除等等。我們可以看一下源碼中 Flags 枚舉類型:

例如 Deletion 代表更新時要對 dom 進行刪除,Placement 代表要進行添加或者替換等等。

// packages/react-reconciler/src/ReactFiberFlags.js
export type Flags = number;
export const NoFlags = /*                      */ 0b000000000000000000;
export const PerformedWork = /*                */ 0b000000000000000001;
export const Placement = /*                    */ 0b000000000000000010;
export const Update = /*                       */ 0b000000000000000100;
export const PlacementAndUpdate = /*           */ 0b000000000000000110;
export const Deletion = /*                     */ 0b000000000000001000;
export const ContentReset = /*                 */ 0b000000000000010000;
export const Callback = /*                     */ 0b000000000000100000;
export const DidCapture = /*                   */ 0b000000000001000000;
export const Ref = /*                          */ 0b000000000010000000;
export const Snapshot = /*                     */ 0b000000000100000000;
export const Passive = /*                      */ 0b000000001000000000;
export const PassiveUnmountPendingDev = /*     */ 0b000010000000000000;
export const Hydrating = /*                    */ 0b000000010000000000;
export const HydratingAndUpdate = /*           */ 0b000000010000000100;
export const LifecycleEffectMask = /*          */ 0b000000001110100100;
export const HostEffectMask = /*               */ 0b000000011111111111;
export const Incomplete = /*                   */ 0b000000100000000000;
export const ShouldCapture = /*                */ 0b000001000000000000;
export const ForceUpdateForLegacySuspense = /* */ 0b000100000000000000;
export const PassiveStatic = /*                */ 0b001000000000000000;
export const BeforeMutationMask = /*           */ 0b000000001100001010;
export const MutationMask = /*                 */ 0b000000010010011110;
export const LayoutMask = /*                   */ 0b000000000010100100;
export const PassiveMask = /*                  */ 0b000000001000001000;
export const StaticMask = /*                   */ 0b001000000000000000;
export const MountLayoutDev = /*               */ 0b010000000000000000;
export const MountPassiveDev = /*              */ 0b100000000000000000;

Effect List

在 render 階段時,react 會采用深度優(yōu)先遍歷,對 fiber 樹進行遍歷,把每一個有副作用的 fiber 篩選出來,最后構建生成一個只帶副作用的 Effect list 鏈表。和該鏈表相關的字段有 firstEffectnextEffectlastEffect

firstEffect 指向第一個有副作用的 fiber 節(jié)點,lastEffect 指向最后一個有副作用的節(jié)點,中間的節(jié)點全部通過 nextEffect 鏈接,最終形成 Effect 鏈表。

在 commit 階段,React 拿到 Effect list 鏈表中的數據后,根據每一個 fiber 節(jié)點的 flags 類型,對相應的 DOM 進行更改。

其他

其他需要重點關注一下的屬性還有 lanealternate。

lane

lane 代表 react 要執(zhí)行的 fiber 任務的優(yōu)先級,通過這個字段,render 階段 react 確定應該優(yōu)先將哪些任務提交到 commit 階段去執(zhí)行。

我們看一下源碼中 lane 的枚舉值:

// packages/react-reconciler/src/ReactFiberLane.js
InputDiscreteHydrationLane: Lane = /*                   */ 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = /*                    */ 0b0000000000000000000000000011000;
const InputContinuousHydrationLane: Lane = /*           */ 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = /*                  */ 0b0000000000000000000000011000000;
export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000100000000;
export const DefaultLanes: Lanes = /*                   */ 0b0000000000000000000111000000000;
const TransitionHydrationLane: Lane = /*                */ 0b0000000000000000001000000000000;
const TransitionLanes: Lanes = /*                       */ 0b0000000001111111110000000000000;
const RetryLanes: Lanes = /*                            */ 0b0000011110000000000000000000000;
export const SomeRetryLane: Lanes = /*                  */ 0b0000010000000000000000000000000;
export const SelectiveHydrationLane: Lane = /*          */ 0b0000100000000000000000000000000;
const NonIdleLanes = /*                                 */ 0b0000111111111111111111111111111;
export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;
const IdleLanes: Lanes = /*                             */ 0b0110000000000000000000000000000;
export const OffscreenLane: Lane = /*                   */ 0b1000000000000000000000000000000;

同 Flags 的枚舉值一樣,Lanes 也是用 31 位的二進制數表示,表示了 31 條賽道,位數越小的賽道,代表的優(yōu)先級越高。

例如 InputDiscreteHydrationLane、InputDiscreteLanesInputContinuousHydrationLane 等用戶交互引起的更新的優(yōu)先級較高,DefaultLanes 這種請求數據引起更新的優(yōu)先級中等,而 OffscreenLane、IdleLanes 這種優(yōu)先級較低。

優(yōu)先級越低的任務,在 render 階段越容易被打斷,commit 執(zhí)行的時機越靠后。

alternate

當 react 的狀態(tài)發(fā)生更新時,當前頁面所對應的 fiber 樹稱為 current Fiber,同時 react 會根據新的狀態(tài)構建一顆新的 fiber 樹,稱為 workInProgress Fiber。current Fiber 中每個 fiber 節(jié)點通過 alternate 字段,指向 workInProgress Fiber 中對應的 fiber 節(jié)點。同樣 workInProgress Fiber 中的 fiber

節(jié)點的 alternate 字段也會指向 current Fiber 中對應的 fiber 節(jié)點。

fiber 樹的構建與更新

下面我們結合源碼,來看一下實際工作過程中 fiber 樹的構建與更新過程。

mount 過程

react 首次 mount 開始執(zhí)行時,以 ReactDOM.render 為入口函數,會經過如下一系列的函數調用:ReactDOM.render ——> legacyRenderSubtreeIntoContainer ——> legacyCreateRootFromDOMContainer ——> createLegacyRoot ——> ReactDOMBlockingRoot ——> ReactDOMRoot ——> createRootImpl ——> createContainer ——> createFiberRoot ——> createHostRootFiber ——> createFiber

createFiber 函數中,調用 FiberNode 構造函數,創(chuàng)建了 rootFiber,它是 react 應用的根 fiber:

// packages/react-reconciler/src/ReactFiber.old.js
const createFiber = function(
  tag: WorkTag,  pendingProps: mixed,  key: null | string,  mode: TypeOfMode,
): Fiber {
  return new FiberNode(tag, pendingProps, key, mode);
};

createFiberRoot 函數中,調用 FiberRootNode 構造函數,創(chuàng)建了 fiberRoot,它指向真實根 dom 節(jié)點。

// packages/react-reconciler/src/ReactFiberRoot.old.js
export function createFiberRoot(
  containerInfo: any,  tag: RootTag,  hydrate: boolean,  hydrationCallbacks: null | SuspenseHydrationCallbacks,
): FiberRoot {
  const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
  if (enableSuspenseCallback) {
    root.hydrationCallbacks = hydrationCallbacks;
  }
  const uninitializedFiber = createHostRootFiber(tag);
  root.current = uninitializedFiber;
  uninitializedFiber.stateNode = root;
  initializeUpdateQueue(uninitializedFiber);
  return root;
}

另外 createFiberRoot 函數中,還讓 rootFiber 的 stateNode 字段指向了 fiberRoot,fiberRoot 的 current 字段指向了 rootFiber。從而一顆最原始的 fiber 樹根節(jié)點就創(chuàng)建完成了:

上面的 rootFiber 和 fiberRoot 創(chuàng)建完成后,react 就會根據 jsx 的內容去創(chuàng)建詳細的 dom 樹了,例如有如下的 jsx:

<div id="root">
  <div id="a1">
    <div id="b1">
      <div id="c1">
        <div id="d1"></div>
        <div id="d2"></div>
        <div id="d3"></div>
      </div>
      <div id="c2"></div>
    </div>
  </div>
</div>

react 對于 fiber 結構的創(chuàng)建和更新,都是采用深度優(yōu)先遍歷,從 rootFiber(此處對應id為root的節(jié)點)開始,首先創(chuàng)建 child a1,然后發(fā)現 a1 有子節(jié)點 b1,繼續(xù)對 b1 進行遍歷,b1 有子節(jié)點 c1,再去創(chuàng)建 c1 的子節(jié)點 d1、d2、d3,直至發(fā)現 d1、d2、d3 都沒有子節(jié)點來了,再回去創(chuàng)建 c2.

上面的過程,每個節(jié)點開始創(chuàng)建時,執(zhí)行 beginWork 流程,直至該節(jié)點的所有子孫節(jié)點都創(chuàng)建(更新)完成后,執(zhí)行 completeWork 流程,過程的圖示如下:

update 過程

update 時,react 會根據新的 jsx 內容創(chuàng)建新的 workInProgress fiber,還是通過深度優(yōu)先遍歷,對發(fā)生改變的 fiber 打上不同的 flags 副作用標簽,并通過 firstEffect、nextEffect 等字段形成 Effect List 鏈表。

例如上面的 jsx 結構,發(fā)生了如下的更新:

<div id="root">
  <div id="a1">
    <div id="b1">
      <div id="c1">
        <div id="d1"></div>
-       <div id="d2"></div>
-       <div id="d3"></div>
      </div>
-     <div id="c2"></div>
+     <div id="c2">new content</div>
    </div>
  </div>
</div>

react 會根據新的 jsx 解析后的內容,調用 createWorkInProgress 函數創(chuàng)建 workInProgress fiber,對其標記副作用:

// packages/react-reconciler/src/ReactFiber.old.js
export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
  let workInProgress = current.alternate;
  if (workInProgress === null) { // 區(qū)分 mount 還是 update
    workInProgress = createFiber(
      current.tag,
      pendingProps,
      current.key,
      current.mode,
    );
    workInProgress.elementType = current.elementType;
    workInProgress.type = current.type;
    workInProgress.stateNode = current.stateNode;
    if (__DEV__) {
      workInProgress._debugID = current._debugID;
      workInProgress._debugSource = current._debugSource;
      workInProgress._debugOwner = current._debugOwner;
      workInProgress._debugHookTypes = current._debugHookTypes;
    }
    workInProgress.alternate = current;
    current.alternate = workInProgress;
  } else {
    workInProgress.pendingProps = pendingProps;
    workInProgress.type = current.type;
    workInProgress.subtreeFlags = NoFlags;
    workInProgress.deletions = null;
    if (enableProfilerTimer) {
      workInProgress.actualDuration = 0;
      workInProgress.actualStartTime = -1;
    }
  }
  // 重置所有的副作用
  workInProgress.flags = current.flags & StaticMask;
  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;
  if (enableProfilerTimer) {
    workInProgress.selfBaseDuration = current.selfBaseDuration;
    workInProgress.treeBaseDuration = current.treeBaseDuration;
  }
  if (__DEV__) {
    workInProgress._debugNeedsRemount = current._debugNeedsRemount;
    switch (workInProgress.tag) {
      case IndeterminateComponent:
      case FunctionComponent:
      case SimpleMemoComponent:
        workInProgress.type = resolveFunctionForHotReloading(current.type);
        break;
      case ClassComponent:
        workInProgress.type = resolveClassForHotReloading(current.type);
        break;
      case ForwardRef:
        workInProgress.type = resolveForwardRefForHotReloading(current.type);
        break;
      default:
        break;
    }
  }
  return workInProgress;
}

最終生成的 workInProgress fiber 圖示如下:

然后如上面所說,current fiber 和 workInProgress fiber 中對應的 alternate 會相互指向,然后 workInProgress fiber 完全創(chuàng)建完成后,fiberRoot 的 current 字段的指向會從 current fiber 中的 rootFiber 改為 workInProgress fiber 中的 rootFiber:

總結

本章講解了 fiber 出現的主要原因、fiber 節(jié)點中主要的屬性以及 fiber 樹是如何構建與更新的。

到此這篇關于React Fiber原理深入分析的文章就介紹到這了,更多相關React Fiber內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • React?useEffect異步操作常見問題小結

    React?useEffect異步操作常見問題小結

    本文主要介紹了React?useEffect異步操作常見問題小結,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-06-06
  • React特征Form?單向數據流示例詳解

    React特征Form?單向數據流示例詳解

    這篇文章主要為大家介紹了React特征Form?單向數據流示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • React性能優(yōu)化系列之減少props改變的實現方法

    React性能優(yōu)化系列之減少props改變的實現方法

    這篇文章主要介紹了React性能優(yōu)化系列之減少props改變的實現方法,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-01-01
  • React forwardRef的使用方法及注意點

    React forwardRef的使用方法及注意點

    React.forwardRef的API中ref必須指向dom元素而不是React組件,通過一段示例代碼給大家介紹了React forwardRef使用方法及注意點還有一些特殊情況分析,感興趣的朋友跟隨小編一起看看吧
    2021-06-06
  • 淺談python的函數知識

    淺談python的函數知識

    這篇文章主要為大家介紹了python的函數,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2021-11-11
  • 詳解使用webpack+electron+reactJs開發(fā)windows桌面應用

    詳解使用webpack+electron+reactJs開發(fā)windows桌面應用

    這篇文章主要介紹了詳解使用webpack+electron+reactJs開發(fā)windows桌面應用,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2019-02-02
  • react-redux多個組件數據共享的方法

    react-redux多個組件數據共享的方法

    這篇文章主要介紹了react-redux多個組件數據共享的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-08-08
  • React hook超詳細教程

    React hook超詳細教程

    Hook是React16.8的新增特性。它可以讓你在不編寫class的情況下使用state以及其他的React特性,這篇文章主要介紹了React hook的使用
    2022-10-10
  • axios請求響應數據加解密封裝實現詳解

    axios請求響應數據加解密封裝實現詳解

    這篇文章主要為大家介紹了axios請求響應數據加解密封裝實現詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • React實現動效彈窗組件

    React實現動效彈窗組件

    最近在使用react開發(fā)項目,遇到這樣一個需求實現一個帶有動效的 React 彈窗組件,如果不考慮動效,很容易實現,接下來小編通過本文給大家介紹React實現動效彈窗組件的實現代碼,一起看看吧
    2021-06-06

最新評論