日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

React超詳細講述Fiber的使用_React

作者:折桂懷橘 ? 更新時間: 2023-04-14 編程語言

Fiber

概念

JavaScript引擎和頁面渲染引擎兩個線程是互斥的,當其中一個線程執行時,另一個線程只能掛起等待

如果 JavaScript 線程長時間地占用了主線程,那么渲染層面的更新就不得不長時間地等待,界面長時間不更新,會導致頁面響應度變差,用戶可能會感覺到卡頓

破解JavaScript中同步操作時間過長的方法其實很簡單——分片。

把一個耗時長的任務分成很多小片,每一個小片的運行時間很短,雖然總時間依然很長,但是在每個小片執行完之后,都給其他任務一個執行的機會,這樣唯一的線程就不會被獨占,其他任務依然有運行的機會。

React Fiber把更新過程碎片化,每執行完一段更新過程,就把控制權交還給React負責任務協調的模塊,看看有沒有其他緊急任務要做,如果沒有就繼續去更新,如果有緊急任務,那就去做緊急任務。

維護每一個分片的數據結構,就是Fiber。

一個 Fiber 代表一個工作單元。

在react中,主要做了以下的操作:

  • 為每個增加了優先級,優先級高的任務可以中斷低優先級的任務。然后再重新,注意是重新執行優先級低的任務
  • 增加了異步任務,調用requestIdleCallback api,瀏覽器空閑的時候執行
  • dom diff樹變成了鏈表,一個dom對應兩個fiber(一個鏈表),對應兩個隊列,這都是為找到被中斷的任務,重新執行

從架構角度來看,Fiber 是對 React核心算法(即調和過程)的重寫

從編碼角度來看,Fiber是 React內部所定義的一種數據結構,它是 Fiber樹結構的節點單位,也就是 React 16 新架構下的虛擬DOM

結構

type Fiber = {
  // 用于標記fiber的WorkTag類型,主要表示當前fiber代表的組件類型如FunctionComponent、ClassComponent等
  tag: WorkTag,
  // ReactElement里面的key
  key: null | string,
  // ReactElement.type,調用`createElement`的第一個參數
  elementType: any,
  // The resolved function/class/ associated with this fiber.
  // 表示當前代表的節點類型
  type: any,
  // 表示當前FiberNode對應的element組件實例
  stateNode: any,
  // 指向他在Fiber節點樹中的`parent`,用來在處理完這個節點之后向上返回
  return: Fiber | null,
  // 指向自己的第一個子節點
  child: Fiber | null,
  // 指向自己的兄弟結構,兄弟節點的return指向同一個父節點
  sibling: Fiber | null,
  index: number,
  ref: null | (((handle: mixed) => void) & { _stringRef: ?string }) | RefObject,
  // 當前處理過程中的組件props對象
  pendingProps: any,
  // 上一次渲染完成之后的props
  memoizedProps: any,
  // 該Fiber對應的組件產生的Update會存放在這個隊列里面
  updateQueue: UpdateQueue<any> | null,
  // 上一次渲染的時候的state
  memoizedState: any,
  // 一個列表,存放這個Fiber依賴的context
  firstContextDependency: ContextDependency<mixed> | null,
  mode: TypeOfMode,
  // Effect
  // 用來記錄Side Effect
  effectTag: SideEffectTag,
  // 單鏈表用來快速查找下一個side effect
  nextEffect: Fiber | null,
  // 子樹中第一個side effect
  firstEffect: Fiber | null,
  // 子樹中最后一個side effect
  lastEffect: Fiber | null,
  // 代表任務在未來的哪個時間點應該被完成,之后版本改名為 lanes
  expirationTime: ExpirationTime,
  // 快速確定子樹中是否有不在等待的變化
  childExpirationTime: ExpirationTime,
  // fiber的版本池,即記錄fiber更新過程,便于恢復
  alternate: Fiber | null,
}

// 指向父級Fiber節點
this.return = null;
// 指向子Fiber節點
this.child = null;
// 指向右邊第一個兄弟Fiber節點
this.sibling = null;

Fiber樹的遍歷是這樣發生的深度遍歷

開始:Fiber 從最上面的 React 元素開始遍歷,并為其創建一個 fiber 節點。

子節點:然后,它轉到子元素,為這個元素創建一個 fiber 節點。這樣繼續下去直到在沒有孩子

兄弟節點: 現在,它檢查是否有兄弟節點元素。如果有,它就遍歷兄弟節點元素,然后再到兄弟姐妹的葉子元素。

返回:如果沒有兄弟節點,那么它就返回到父節點。

window.requestIdleCallback()

該方法將在瀏覽器的空閑時段內調用的函數排隊。方法提供 deadline,即任務執行限制時間,以切分任務,避免長時間執行,阻塞UI渲染而導致掉幀;

【安排低優先級或非必要的函數在幀結束時的空閑時間被調用】

requestAnimationFrame

安排高優先級的函數在下一個動畫幀之前被調用

Fiber是如何工作的

  • ReactDOM.render() 和 setState 的時候開始創建更新。
  • 將創建的更新加入任務隊列,等待調度。
  • 在 requestIdleCallback 空閑時執行任務。
  • 從根節點開始遍歷 Fiber Node,并且構建 WokeInProgress Tree。
  • 生成 effectList。
  • 根據 EffectList 更新 DOM。

當調用render和setState方法進行組件渲染和更新的時候,react會經歷倆個階段:reconciler和render階段:

  • 調和階段(Reconciler):官方解釋。React 會自頂向下通過遞歸,遍歷新數據生成新的 Virtual DOM,然后通過 Diff 算法,找到需要變更的元素(Patch),放到更新隊列里面去。
  • 渲染階段(Renderer):遍歷更新隊列,通過調用宿主環境的API,實際更新渲染對應元素。宿主環境,比如 DOM、Native、WebGL 等。

React15 最大的問題就是,Reconciler(協調)階段產生產生虛擬DOM是通過深度優先遞歸的,并且中途不可間斷。所以假如虛擬DOM很深的話,由于 JS線程和瀏覽器 GUI 線程是互斥的,處理 js 的時間過長,會導致瀏覽器刷新的時候掉幀,造成卡頓。

而 React16則實現了異步的可中斷的更新。

Fiber 使用 requestAnimationFrame 來處理優先級較高的更新,使用 requestIdleCallback 處理優先級較低的更新。因此,在調度工作時,Fiber 檢查當前更新的優先級和 deadline (幀結束后的自由時間)。

如果優先級高于待處理的工作,或者沒有 截止日期 或者截止日期尚未到達,Fiber 可以在一幀之后安排多個工作單元。而下一組工作單元會被帶到更多的幀上。這就是使 Fiber 有可能暫停、重用和中止工作單元的原因。

那么,讓我們看看在預定的工作中實際發生了什么。有兩個階段來完成工作。render 和 commit。

渲染階段

實際的樹形遍歷和 deadline 的使用發生在這個階段。這是 Fiber 的內部邏輯,所以在這個階段對 Fiber 樹所做的改變對用戶來說是不可見的。因此,Fiber 可以暫停、中止或分擔多個框架的工作。

我們可以把這個階段稱為協調階段。 fiber 從 fiber 樹的根部開始遍歷,處理每個 fiber 。每一個工作單位都會調用workLoop 函數來執行工作。我們可以把這個工作的處理分成兩個步驟。begin 和 complete 。

開始階段

如果你從 React 代碼庫中找到 workLoop 函數,它就會調用 performUnitOfWork,它把 nextUnitOfWork 作為一個參數,它就只是個工作的單位,將被執行。 performUnitOfWork 函數內部調用 beginWork 函數。這是 fiber 上發生實際工作的地方,而 performUnitOfWork 只是發生迭代的地方。

在 beginWork 函數中,如果 fiber 沒有任何待處理的工作,它就會直接跳出(跳過) fiber 而不進入開始階段。這就是在遍歷大樹時, fiber 跳過已經處理過的 fiber ,直接跳到有待處理工作的 fiber 。如果你看到大的 beginWork 函數代碼塊,我們會發現一個開關塊,根據 fiber 標簽,調用相應的 fiber 更新函數。就像 updateHostComponent 用于宿主組件。這些函數會更新 fiber 。

如果有子 fiber ,beginWork函數返回子 fiber ,如果沒有子 fiber 則返回空。函數 performUnitOfWork 持續迭代并調用子 fiber ,直到葉節點到達。在葉子節點的情況下,beginWork 返回 null,因為沒有任何子節點,performUnitOfWork 函數調用 completeUnitOfWork 函數?,F在讓我們看看完善階段。

完善階段

這個 completeUnitOfWork 函數通過調用一個 completeWork 函數來完成當前單位的工作。如果有的話,completeUnitOfWork 會返回一個同級的 fiber 來執行下一個工作單元,如果沒有工作的話,則會完成 return(parent) fiber 。這將一直持續到返回值為空,也就是說,直到它到達根節點。和 beginWork 一樣,completeWork 也是一個發生實際工作的函數,而 completeUnitOfWork 是用于迭代的。

渲染階段的結果會產生一個效果列表(副作用)。這些效果就像插入、更新或刪除宿主組件的節點,或調用類組件節點的生命周期方法。這些 fiber 被標記為各自的效果標簽。

在渲染階段之后,Fiber 將準備提交更新。

提交階段

這是一個階段,完成的工作將被用來在用戶界面上渲染它。由于這一階段的結果對用戶來說是可見的,所以不能被分成部分渲染。這個階段是一個同步的階段。

在這個階段的開始,Fiber 有已經在 UI 上渲染的 current 樹,finishedWork,或者在渲染階段建立的 workInProgress 樹和效果列表。

effect 列表是 fiber 的鏈表,它有副作用。所以,它是渲染階段的 workInProgress 樹的節點的一個子集,它有副作用(更新)。effect 列表的節點是用 nextEffect 指針鏈接的。

在這個階段調用的函數是 completeRoot。

在這里,workInProgress 樹成為 current 樹,因為它被用來渲染 UI。實際的 DOM 更新,如插入、更新、刪除,以及對生命周期方法的調用或者更新相對應的引用 —— 發生在 effect 列表中的節點上。

這就是 fiber 協調器的工作方式。

結論

這就是 React Fiber 協調器使之有可能將工作分為多個工作單元。它設置每個工作的優先級,并使暫停、重用和中止工作單元成為可能。在 fiber 樹中,單個節點保持跟蹤,這是使上述事情成為可能的需要。每個 fiber 都是一個鏈表的節點,它們通過子、兄弟節點和返回引用連接起來。

  • 有react fiber,為什么不需要vue fiber “我們現在已經知道了react fiber是在彌補更新時“無腦”刷新,不夠精確帶來的缺陷?!眆iber不是用來彌補無腦刷新的,fibe是用來讓原來同步的調用顆粒化的,解決無腦刷新你應該用meno
  • vue不需要fiber是因為他使用nextTick來異步決定什么時候執行renderfunction 本質上思路是和react一致的和響應式原理沒有半毛錢關系

原文鏈接:https://blog.csdn.net/weixin_42910765/article/details/128832862

欄目分類
最近更新