React Fiber 原理介紹

歡迎關注個人公衆號睿Talk,獲取我最新的文章:
clipboard.pngjavascript

1、前言

在 React Fiber 架構面世一年多後,最近 React 又發佈了最新版 16.8.0,又一激動人心的特性:React Hooks 正式上線,讓我升級 React 的意願愈來愈強烈了。在升級以前,不妨回到原點,瞭解下人才輩出的 React 團隊爲何要大費周章,重寫 React 架構,而 Fiber 又是個什麼概念。java

2、React 15 的問題

在頁面元素不少,且須要頻繁刷新的場景下,React 15 會出現掉幀的現象。請看如下例子:
https://claudiopro.github.io/...react

clipboard.png

其根本緣由,是大量的同步計算任務阻塞了瀏覽器的 UI 渲染。默認狀況下,JS 運算、頁面佈局和頁面繪製都是運行在瀏覽器的主線程當中,他們之間是互斥的關係。若是 JS 運算持續佔用主線程,頁面就無法獲得及時的更新。當咱們調用setState更新頁面的時候,React 會遍歷應用的全部節點,計算出差別,而後再更新 UI。整個過程是一鼓作氣,不能被打斷的。若是頁面元素不少,整個過程佔用的時機就可能超過 16 毫秒,就容易出現掉幀的現象。git

針對這一問題,React 團隊從框架層面對 web 頁面的運行機制作了優化,獲得很好的效果。github

clipboard.png

3、解題思路

解決主線程長時間被 JS 運算佔用這一問題的基本思路,是將運算切割爲多個步驟,分批完成。也就是說在完成一部分任務以後,將控制權交回給瀏覽器,讓瀏覽器有時間進行頁面的渲染。等瀏覽器忙完以後,再繼續以前未完成的任務。web

舊版 React 經過遞歸的方式進行渲染,使用的是 JS 引擎自身的函數調用棧,它會一直執行到棧空爲止。而Fiber實現了本身的組件調用棧,它以鏈表的形式遍歷組件樹,能夠靈活的暫停、繼續和丟棄執行的任務。實現方式是使用了瀏覽器的requestIdleCallback這一 API。官方的解釋是這樣的:segmentfault

window.requestIdleCallback()會在瀏覽器空閒時期依次調用函數,這就可讓開發者在主事件循環中執行後臺或低優先級的任務,並且不會對像動畫和用戶交互這些延遲觸發但關鍵的事件產生影響。函數通常會按先進先調用的順序執行,除非函數在瀏覽器調用它以前就到了它的超時時間。

有了解題思路後,咱們再來看看 React 具體是怎麼作的。瀏覽器

4、React 的答卷

React 框架內部的運做能夠分爲 3 層:數據結構

  • Virtual DOM 層,描述頁面長什麼樣。
  • Reconciler 層,負責調用組件生命週期方法,進行 Diff 運算等。
  • Renderer 層,根據不一樣的平臺,渲染出相應的頁面,比較常見的是 ReactDOM 和 ReactNative。

此次改動最大的當屬 Reconciler 層了,React 團隊也給它起了個新的名字,叫Fiber Reconciler。這就引入另外一個關鍵詞:Fiber。架構

Fiber 其實指的是一種數據結構,它能夠用一個純 JS 對象來表示:

const fiber = {
    stateNode,    // 節點實例
    child,        // 子節點
    sibling,      // 兄弟節點
    return,       // 父節點
}

爲了加以區分,之前的 Reconciler 被命名爲Stack Reconciler。Stack Reconciler 運做的過程是不能被打斷的,必須一條道走到黑:

clipboard.png

而 Fiber Reconciler 每執行一段時間,都會將控制權交回給瀏覽器,能夠分段執行:

clipboard.png

爲了達到這種效果,就須要有一個調度器 (Scheduler) 來進行任務分配。任務的優先級有六種:

  • synchronous,與以前的Stack Reconciler操做同樣,同步執行
  • task,在next tick以前執行
  • animation,下一幀以前執行
  • high,在不久的未來當即執行
  • low,稍微延遲執行也不要緊
  • offscreen,下一次render時或scroll時才執行

優先級高的任務(如鍵盤輸入)能夠打斷優先級低的任務(如Diff)的執行,從而更快的生效。

Fiber Reconciler 在執行過程當中,會分爲 2 個階段。

clipboard.png

  • 階段一,生成 Fiber 樹,得出須要更新的節點信息。這一步是一個漸進的過程,能夠被打斷。
  • 階段二,將須要更新的節點一次過批量更新,這個過程不能被打斷。

階段一可被打斷的特性,讓優先級更高的任務先執行,從框架層面大大下降了頁面掉幀的機率。

5、Fiber 樹

Fiber Reconciler 在階段一進行 Diff 計算的時候,會生成一棵 Fiber 樹。這棵樹是在 Virtual DOM 樹的基礎上增長額外的信息來生成的,它本質來講是一個鏈表。

clipboard.png

Fiber 樹在首次渲染的時候會一次過生成。在後續須要 Diff 的時候,會根據已有樹和最新 Virtual DOM 的信息,生成一棵新的樹。這顆新樹每生成一個新的節點,都會將控制權交回給主線程,去檢查有沒有優先級更高的任務須要執行。若是沒有,則繼續構建樹的過程:

clipboard.png

若是過程當中有優先級更高的任務須要進行,則 Fiber Reconciler 會丟棄正在生成的樹,在空閒的時候再從新執行一遍。

在構造 Fiber 樹的過程當中,Fiber Reconciler 會將須要更新的節點信息保存在Effect List當中,在階段二執行的時候,會批量更新相應的節點。

6、總結

本文從 React 15 存在的問題出發,介紹 React Fiber 解決問題的思路,並介紹了 Fiber Reconciler 的工做流程。從Stack ReconcilerFiber Reconciler,源碼層面其實就是幹了一件遞歸改循環的事情,往後有機會的話,我再結合源碼做進一步的介紹。

相關文章
相關標籤/搜索