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

學(xué)無(wú)先后,達(dá)者為師

網(wǎng)站首頁(yè) 編程語(yǔ)言 正文

React超詳細(xì)講述Fiber的使用_React

作者:折桂懷橘 ? 更新時(shí)間: 2023-04-14 編程語(yǔ)言

Fiber

概念

JavaScript引擎和頁(yè)面渲染引擎兩個(gè)線程是互斥的,當(dāng)其中一個(gè)線程執(zhí)行時(shí),另一個(gè)線程只能掛起等待

如果 JavaScript 線程長(zhǎng)時(shí)間地占用了主線程,那么渲染層面的更新就不得不長(zhǎng)時(shí)間地等待,界面長(zhǎng)時(shí)間不更新,會(huì)導(dǎo)致頁(yè)面響應(yīng)度變差,用戶可能會(huì)感覺(jué)到卡頓

破解JavaScript中同步操作時(shí)間過(guò)長(zhǎng)的方法其實(shí)很簡(jiǎn)單——分片。

把一個(gè)耗時(shí)長(zhǎng)的任務(wù)分成很多小片,每一個(gè)小片的運(yùn)行時(shí)間很短,雖然總時(shí)間依然很長(zhǎng),但是在每個(gè)小片執(zhí)行完之后,都給其他任務(wù)一個(gè)執(zhí)行的機(jī)會(huì),這樣唯一的線程就不會(huì)被獨(dú)占,其他任務(wù)依然有運(yùn)行的機(jī)會(huì)。

React Fiber把更新過(guò)程碎片化,每執(zhí)行完一段更新過(guò)程,就把控制權(quán)交還給React負(fù)責(zé)任務(wù)協(xié)調(diào)的模塊,看看有沒(méi)有其他緊急任務(wù)要做,如果沒(méi)有就繼續(xù)去更新,如果有緊急任務(wù),那就去做緊急任務(wù)。

維護(hù)每一個(gè)分片的數(shù)據(jù)結(jié)構(gòu),就是Fiber。

一個(gè) Fiber 代表一個(gè)工作單元。

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

  • 為每個(gè)增加了優(yōu)先級(jí),優(yōu)先級(jí)高的任務(wù)可以中斷低優(yōu)先級(jí)的任務(wù)。然后再重新,注意是重新執(zhí)行優(yōu)先級(jí)低的任務(wù)
  • 增加了異步任務(wù),調(diào)用requestIdleCallback api,瀏覽器空閑的時(shí)候執(zhí)行
  • dom diff樹(shù)變成了鏈表,一個(gè)dom對(duì)應(yīng)兩個(gè)fiber(一個(gè)鏈表),對(duì)應(yīng)兩個(gè)隊(duì)列,這都是為找到被中斷的任務(wù),重新執(zhí)行

從架構(gòu)角度來(lái)看,F(xiàn)iber 是對(duì) React核心算法(即調(diào)和過(guò)程)的重寫(xiě)

從編碼角度來(lái)看,F(xiàn)iber是 React內(nèi)部所定義的一種數(shù)據(jù)結(jié)構(gòu),它是 Fiber樹(shù)結(jié)構(gòu)的節(jié)點(diǎn)單位,也就是 React 16 新架構(gòu)下的虛擬DOM

結(jié)構(gòu)

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

// 指向父級(jí)Fiber節(jié)點(diǎn)
this.return = null;
// 指向子Fiber節(jié)點(diǎn)
this.child = null;
// 指向右邊第一個(gè)兄弟Fiber節(jié)點(diǎn)
this.sibling = null;

Fiber樹(shù)的遍歷是這樣發(fā)生的深度遍歷

開(kāi)始:Fiber 從最上面的 React 元素開(kāi)始遍歷,并為其創(chuàng)建一個(gè) fiber 節(jié)點(diǎn)。

子節(jié)點(diǎn):然后,它轉(zhuǎn)到子元素,為這個(gè)元素創(chuàng)建一個(gè) fiber 節(jié)點(diǎn)。這樣繼續(xù)下去直到在沒(méi)有孩子

兄弟節(jié)點(diǎn): 現(xiàn)在,它檢查是否有兄弟節(jié)點(diǎn)元素。如果有,它就遍歷兄弟節(jié)點(diǎn)元素,然后再到兄弟姐妹的葉子元素。

返回:如果沒(méi)有兄弟節(jié)點(diǎn),那么它就返回到父節(jié)點(diǎn)。

window.requestIdleCallback()

該方法將在瀏覽器的空閑時(shí)段內(nèi)調(diào)用的函數(shù)排隊(duì)。方法提供 deadline,即任務(wù)執(zhí)行限制時(shí)間,以切分任務(wù),避免長(zhǎng)時(shí)間執(zhí)行,阻塞UI渲染而導(dǎo)致掉幀;

【安排低優(yōu)先級(jí)或非必要的函數(shù)在幀結(jié)束時(shí)的空閑時(shí)間被調(diào)用】

requestAnimationFrame

安排高優(yōu)先級(jí)的函數(shù)在下一個(gè)動(dòng)畫(huà)幀之前被調(diào)用

Fiber是如何工作的

  • ReactDOM.render() 和 setState 的時(shí)候開(kāi)始創(chuàng)建更新。
  • 將創(chuàng)建的更新加入任務(wù)隊(duì)列,等待調(diào)度。
  • 在 requestIdleCallback 空閑時(shí)執(zhí)行任務(wù)。
  • 從根節(jié)點(diǎn)開(kāi)始遍歷 Fiber Node,并且構(gòu)建 WokeInProgress Tree。
  • 生成 effectList。
  • 根據(jù) EffectList 更新 DOM。

當(dāng)調(diào)用render和setState方法進(jìn)行組件渲染和更新的時(shí)候,react會(huì)經(jīng)歷倆個(gè)階段:reconciler和render階段:

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

React15 最大的問(wèn)題就是,Reconciler(協(xié)調(diào))階段產(chǎn)生產(chǎn)生虛擬DOM是通過(guò)深度優(yōu)先遞歸的,并且中途不可間斷。所以假如虛擬DOM很深的話,由于 JS線程和瀏覽器 GUI 線程是互斥的,處理 js 的時(shí)間過(guò)長(zhǎng),會(huì)導(dǎo)致瀏覽器刷新的時(shí)候掉幀,造成卡頓。

而 React16則實(shí)現(xiàn)了異步的可中斷的更新。

Fiber 使用 requestAnimationFrame 來(lái)處理優(yōu)先級(jí)較高的更新,使用 requestIdleCallback 處理優(yōu)先級(jí)較低的更新。因此,在調(diào)度工作時(shí),F(xiàn)iber 檢查當(dāng)前更新的優(yōu)先級(jí)和 deadline (幀結(jié)束后的自由時(shí)間)。

如果優(yōu)先級(jí)高于待處理的工作,或者沒(méi)有 截止日期 或者截止日期尚未到達(dá),F(xiàn)iber 可以在一幀之后安排多個(gè)工作單元。而下一組工作單元會(huì)被帶到更多的幀上。這就是使 Fiber 有可能暫停、重用和中止工作單元的原因。

那么,讓我們看看在預(yù)定的工作中實(shí)際發(fā)生了什么。有兩個(gè)階段來(lái)完成工作。render 和 commit。

渲染階段

實(shí)際的樹(shù)形遍歷和 deadline 的使用發(fā)生在這個(gè)階段。這是 Fiber 的內(nèi)部邏輯,所以在這個(gè)階段對(duì) Fiber 樹(shù)所做的改變對(duì)用戶來(lái)說(shuō)是不可見(jiàn)的。因此,F(xiàn)iber 可以暫停、中止或分擔(dān)多個(gè)框架的工作。

我們可以把這個(gè)階段稱為協(xié)調(diào)階段。 fiber 從 fiber 樹(shù)的根部開(kāi)始遍歷,處理每個(gè) fiber 。每一個(gè)工作單位都會(huì)調(diào)用workLoop 函數(shù)來(lái)執(zhí)行工作。我們可以把這個(gè)工作的處理分成兩個(gè)步驟。begin 和 complete 。

開(kāi)始階段

如果你從 React 代碼庫(kù)中找到 workLoop 函數(shù),它就會(huì)調(diào)用 performUnitOfWork,它把 nextUnitOfWork 作為一個(gè)參數(shù),它就只是個(gè)工作的單位,將被執(zhí)行。 performUnitOfWork 函數(shù)內(nèi)部調(diào)用 beginWork 函數(shù)。這是 fiber 上發(fā)生實(shí)際工作的地方,而 performUnitOfWork 只是發(fā)生迭代的地方。

在 beginWork 函數(shù)中,如果 fiber 沒(méi)有任何待處理的工作,它就會(huì)直接跳出(跳過(guò)) fiber 而不進(jìn)入開(kāi)始階段。這就是在遍歷大樹(shù)時(shí), fiber 跳過(guò)已經(jīng)處理過(guò)的 fiber ,直接跳到有待處理工作的 fiber 。如果你看到大的 beginWork 函數(shù)代碼塊,我們會(huì)發(fā)現(xiàn)一個(gè)開(kāi)關(guān)塊,根據(jù) fiber 標(biāo)簽,調(diào)用相應(yīng)的 fiber 更新函數(shù)。就像 updateHostComponent 用于宿主組件。這些函數(shù)會(huì)更新 fiber 。

如果有子 fiber ,beginWork函數(shù)返回子 fiber ,如果沒(méi)有子 fiber 則返回空。函數(shù) performUnitOfWork 持續(xù)迭代并調(diào)用子 fiber ,直到葉節(jié)點(diǎn)到達(dá)。在葉子節(jié)點(diǎn)的情況下,beginWork 返回 null,因?yàn)闆](méi)有任何子節(jié)點(diǎn),performUnitOfWork 函數(shù)調(diào)用 completeUnitOfWork 函數(shù)。現(xiàn)在讓我們看看完善階段。

完善階段

這個(gè) completeUnitOfWork 函數(shù)通過(guò)調(diào)用一個(gè) completeWork 函數(shù)來(lái)完成當(dāng)前單位的工作。如果有的話,completeUnitOfWork 會(huì)返回一個(gè)同級(jí)的 fiber 來(lái)執(zhí)行下一個(gè)工作單元,如果沒(méi)有工作的話,則會(huì)完成 return(parent) fiber 。這將一直持續(xù)到返回值為空,也就是說(shuō),直到它到達(dá)根節(jié)點(diǎn)。和 beginWork 一樣,completeWork 也是一個(gè)發(fā)生實(shí)際工作的函數(shù),而 completeUnitOfWork 是用于迭代的。

渲染階段的結(jié)果會(huì)產(chǎn)生一個(gè)效果列表(副作用)。這些效果就像插入、更新或刪除宿主組件的節(jié)點(diǎn),或調(diào)用類組件節(jié)點(diǎn)的生命周期方法。這些 fiber 被標(biāo)記為各自的效果標(biāo)簽。

在渲染階段之后,F(xiàn)iber 將準(zhǔn)備提交更新。

提交階段

這是一個(gè)階段,完成的工作將被用來(lái)在用戶界面上渲染它。由于這一階段的結(jié)果對(duì)用戶來(lái)說(shuō)是可見(jiàn)的,所以不能被分成部分渲染。這個(gè)階段是一個(gè)同步的階段。

在這個(gè)階段的開(kāi)始,F(xiàn)iber 有已經(jīng)在 UI 上渲染的 current 樹(shù),finishedWork,或者在渲染階段建立的 workInProgress 樹(shù)和效果列表。

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

在這個(gè)階段調(diào)用的函數(shù)是 completeRoot。

在這里,workInProgress 樹(shù)成為 current 樹(shù),因?yàn)樗挥脕?lái)渲染 UI。實(shí)際的 DOM 更新,如插入、更新、刪除,以及對(duì)生命周期方法的調(diào)用或者更新相對(duì)應(yīng)的引用 —— 發(fā)生在 effect 列表中的節(jié)點(diǎn)上。

這就是 fiber 協(xié)調(diào)器的工作方式。

結(jié)論

這就是 React Fiber 協(xié)調(diào)器使之有可能將工作分為多個(gè)工作單元。它設(shè)置每個(gè)工作的優(yōu)先級(jí),并使暫停、重用和中止工作單元成為可能。在 fiber 樹(shù)中,單個(gè)節(jié)點(diǎn)保持跟蹤,這是使上述事情成為可能的需要。每個(gè) fiber 都是一個(gè)鏈表的節(jié)點(diǎn),它們通過(guò)子、兄弟節(jié)點(diǎn)和返回引用連接起來(lái)。

  • 有react fiber,為什么不需要vue fiber “我們現(xiàn)在已經(jīng)知道了react fiber是在彌補(bǔ)更新時(shí)“無(wú)腦”刷新,不夠精確帶來(lái)的缺陷。”fiber不是用來(lái)彌補(bǔ)無(wú)腦刷新的,fibe是用來(lái)讓原來(lái)同步的調(diào)用顆粒化的,解決無(wú)腦刷新你應(yīng)該用meno
  • vue不需要fiber是因?yàn)樗褂胣extTick來(lái)異步?jīng)Q定什么時(shí)候執(zhí)行renderfunction 本質(zhì)上思路是和react一致的和響應(yīng)式原理沒(méi)有半毛錢(qián)關(guān)系

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

欄目分類
最近更新