React Fiber 簡介—— React 背後的算法
在這篇文章中,我們將了解React Fiber —— React 背後的核心算法。 React Fiber 是React 16 中新的協調算法。你很可能聽說過React 15 中的不同的渲染器,如DOM、Native 和Android 視圖,都會共享同一個協調器,所以稱它為
讓我們趕緊看看什麼是React Fiber。
介紹
React Fiber 是針對就協調器重寫的完全向後兼容的一個版本。 React 的這種新的協調算法被稱為Fiber Reconciler。這個名字來自於我們將在後面的章節中詳細介紹
Fiber 協調器的主要目標是增量渲染,更好更平滑地渲染UI 動畫和手勢,以及用戶互動的響應性。協調器還允許你將工作分為多個塊,並將渲染工作分為多個幀。它還增加了為每個工作單元定義優先級的能力,以及暫停、重複使用和中止工作的能力。
React 的其他一些特性包括從一個渲染函數返回多個元素,支持更好的錯誤處理(我們可以使用
在計算新的渲染更新時,React 會多次回訪主線程。因此,高優先級的工作可以跳過低優先級的工作。 React 在內部為每個更新定義了優先級。
在進入技術細節之前,我建議你學習以下術語,這將有助於理解React Fiber。
先決條件
協調
正如官方當用戶界面第一次渲染時,React 創建了一個節點樹。每個單獨的節點都代表React 元素。它創建了一個虛擬樹(被稱為在來自用戶界面的任何更新之後,它遞歸地比較兩棵樹的每一個樹節點。然後,累積的變化被傳遞給渲染器。
調度
正如官方應該有一個選項,將高優先級的工作優先於低優先級的工作。在舊的堆棧協調器實現中,遞歸遍歷和調用整個更新的樹的渲染方法都發生在單個流程中,這可能會導致丟幀。
調度可以是基於時間或基於優先級的。更新應該根據deadline 來安排,高優先級的工作應該被安排在低優先級的工作之上。
requestIdleCallback(請求閒置回調)
requestAnimationFrame類似地,
requestIdleCallback(lowPriorityWork);
這裡展示了lowPriorityWork
functionlowPriorityWork(deadline){while(deadline.timeRemaining()>0&&workList.length>0)performUnitOfWork();if(workList.length>0)requestIdleCallback(lowPriorityWork);}
當這個回調函數被調用時,它得到參數正如你在上面的片段中看到的,如果這個時間大於零,我們可以做一些必要的工作。而如果工作沒有完成,我們可以在下一幀的最後一行再次安排工作。
所以,現在我們可以繼續研究
Fiber 的結構
一個它代表React 元素或DOM 樹的一個節點。它是一個工作單位。相比之下,Fiber 是React Fiber 的協調器。
這個例子展示了一個簡單的React 組件,在
functionApp(){return(<divclassName="wrapper"><divclassName="list"><divclassName="list_item">ListitemA</div><divclassName="list_item">ListitemB</div></div><divclassName="section"><button>Add</button><span>No.ofitems:2</span></div></div>);}ReactDOM.render(<App/>,document.getElementById("root"));
這是一個簡單的組件,為我們從組件狀態得到的數據顯示一個列表項。 (我把
如前所述,在第一次渲染時,React 會瀏覽每個React 元素並創建一棵(我們將在後面的章節中看到它是如何創建這個樹的)。
它為每個單獨的React 元素創建一個它將為div 創建一個然後,為具有讓我們為兩個列表項的
在後面的部分,我們將看到它是如何迭代的,以及樹的最終結構。雖然我們稱之為樹,但React Fiber 創建了一個節點的鍊錶,其中每個節點都是一個並且在父、子和兄弟姐妹之間存在著一種關係。 React 使用一個所以,在上面的例子中,
那麼,這個
下面是React 代碼庫中對fiber 類型的定義。我刪除了一些額外的你可以在
exporttypeFiber={// 識別fiber 類型的標籤。tag:TypeOfWork,// child 的唯一標識符。key:null|string,// 元素的值。類型,用於在協調child 的過程中保存身份。elementType:any,// 與該fiber 相關的已解決的function / class。type:any,// 與該fiber 相關的當前狀態。stateNode:any,// fiber 剩餘的字段// 處理完這個問題後要返回的fiber。// 這實際上就是parent。// 它在概念上與堆棧幀的返回地址相同。return:Fiber|null,// 單鍊錶樹結構。child:Fiber|null,sibling:Fiber|null,index:number,// 最後一次用到連接該節點的引用。ref:|null|(((handle:mixed)=>void)&{_stringRef:?string,...})|RefObject,// 進入處理這個fiber 的數據。 Arguments、Props。pendingProps:any,// 一旦我們重載標籤,這種類型將更加具體。memoizedProps:any,// 用來創建輸出的道具。// 一個狀態更新和回調的隊列。updateQueue:mixed,// 用來創建輸出的狀態memoizedState:any,mode:TypeOfMode,// EffecteffectTag:SideEffectTag,subtreeTag:SubtreeTag,deletions:Array<Fiber>|null,// 單鍊錶的快速到下一個fiber 的副作用。nextEffect:Fiber|null,// 在這個子樹中,第一個和最後一個有副作用的fiber。// 這使得我們在復用這個fiber 內所做的工作時,可以復用鍊錶的一個片斷。firstEffect:Fiber|null,lastEffect:Fiber|null,// 這是一個fiber 的集合版本。每個被更新的fiber 最終都是成對的。// 有些情況下,如果需要的話,我們可以清理這些成對的fiber 來節省內存。alternate:Fiber|null,};
React Fiber 是如何工作的?
接下來,我們將看到React Fiber 是如何創建鍊錶樹的,以及當有更新時它會做什麼。
在此之前,讓我們解釋一下什麼是
當前被刷新用來渲染用戶界面的樹,被稱為每當有更新時,Fiber 會建立一個React 在這個一旦這個

Fiber 樹的遍歷是這樣發生的。
- 開始:Fiber 從最上面的React 元素開始遍歷,並為其創建一個fiber 節點。
- 子節點:然後,它轉到子元素,為這個元素創建一個fiber 節點。這樣繼續下去,直到到達葉子元素。
- 兄弟節點: 現在,它檢查是否有兄弟節點元素。如果有,它就遍歷兄弟節點元素,然後再到兄弟姐妹的葉子元素。
- 返回:如果沒有兄弟節點,那麼它就返回到父節點。
每個這些是

讓我們舉同樣的例子,我們先給特定React 元素的
functionApp(){// Appreturn(<divclassName="wrapper">{" "}// W<div className="list">{" "}// L<div className="list_item">List item A</div> // LA<divclassName="list_item">ListitemB</div> //LB</div><divclassName="section">{" "}// S<button>Add</button> // SB<span>No.ofitems:2</span> //SS</div></div>);}ReactDOM.render(<App/>,document.getElementById("root"));// HostRoot
首先,我們將快速介紹創建樹時的掛載階段,之後,我們將了解樹更新後詳細邏輯。
初始渲染
App 組件被渲染在root div 中,它的id 是
在進一步遍歷之前,React Fiber 創建一個根每個在我們這裡,它是如果我們在DOM 中導入多個React 應用,可以有多個根節點。
在第一次渲染之前,不會有任何樹。 React Fiber 遍歷每個組件的渲染函數的輸出,並為每個React 元素在樹上創建一個它用React 元素可以是一個類組件,也可以是一個宿主組件,如div 或span。對於類組件,它創建一個實例,而對於宿主組件,它從React 元素中獲得數據和props。
因此,正如例子中所示,它創建了一個再往前走,它又創建了一個fiber
因此,這就是最終的

這就是樹節點是如何使用子節點、同級節點和返回指針連接的。
更新階段
現在,讓我們來談談第二種情況,例如由於setState 而導致的更新。
所以,在這個時候,Fiber 已經有了對於每次更新,它都會建立一個它從根與初始渲染階段不同,它不會為每個React 元素創建一個新的它只是為該React 元素使用預先存在的
早些時候,在React 15 中,堆棧協調器是同步的。所以,一個更新會遞歸地遍歷整個樹,並製作一個樹的副本。假設在這之間,如果有其他的更新比它的優先級更高,那麼就沒有機會中止或暫停第一個更新並執行第二個更新。
React Fiber 將更新劃分為工作單元。它可以為每個工作單元分配優先級,並有能力暫停、重用或在不需要時中止工作單元。 React Fiber 將工作分為多個工作單位,也就是它將工作安排在多個框架中,並使用來自每個更新都有其優先級的定義,如動畫,或用戶輸入的優先級高於從獲取的數據中渲染項目的列表。 Fiber 使用因此,在調度工作時,Fiber 檢查當前更新的優先級和
如果優先級高於待處理的工作,或者沒有 而下一組工作單元會被帶到更多的幀上。這就是使Fiber 有可能暫停、重用和中止工作單元的原因。
那麼,讓我們看看在預定的工作中實際發生了什麼。有兩個階段來完成工作。 render
渲染階段
實際的樹形遍歷和這是Fiber 的內部邏輯,所以在這個階段對Fiber 樹所做的改變對用戶來說是不可見的。因此,Fiber 可以暫停、中止或分擔多個框架的工作。
我們可以把這個階段稱為協調階段。 fiber每一個工作單位都會調用我們可以把這個工作的處理分成兩個步驟。 begin
開始階段
如果你從React 代碼庫中找到performUnitOfWork這是
在這就是在遍歷大樹時,如果你看到大的就像這些函數會更新
如果有子函數在葉子節點的情況下,現在讓我們看看完善階段。
完善階段
這個如果有的話,這將一直持續到返回值為空,也就是說,直到它到達根節點。和
渲染階段的結果會產生一個效果列表(副作用)。這些效果就像插入、更新或刪除宿主組件的節點,或調用類組件節點的生命週期方法。這些
在渲染階段之後,Fiber 將準備提交更新。
提交階段
這是一個階段,完成的工作將被用來在用戶界面上渲染它。由於這一階段的結果對用戶來說是可見的,所以不能被分成部分渲染。這個階段是一個同步的階段。
在這個階段的開始,Fiber 有已經在UI 上渲染的
effect 列表是所以,它是渲染階段的effect 列表的節點是用
在這個階段調用的函數是
在這裡,實際的DOM 更新,如插入、更新、刪除,以及對生命週期方法的調用或者更新相對應的引用—— 發生在effect 列表中的節點上。
這就是
結論
這就是React Fiber 協調器使之有可能將工作分為多個工作單元。它設置每個工作的優先級,並使暫停、重用和中止工作單元成為可能。在每個
你可以找到更多關於React Fiber 的訊息:
相關文章
- 使用Formik 在React 中構建動態表單—— 更快、更好
- 用Hooks 和函數式編程編寫更簡潔、高效的代碼
原文鏈接: