我所理解的React Fiber架構

前言

在 react16 沒有正式公佈之前,業界的人員覺得此次的 react16 就叫作 Fiber,足以說明 Fiber 的重要性。fiber 在英文中意爲纖維,此處意爲比線程還細的單位。Facebook 取名 Fiber 的意思是爲了描述一個比線程更小單位的渲染機制。用一句話來描述我所理解的 Fiber 架構:react

新的 Fiber 架構改變了以前 react 同步的組件渲染機制,使原來同步渲染的組件如今能夠異步化,可中途中斷渲染,執行更高優先級的任務,用戶體驗更好。git

爲了解決什麼樣的問題

每次更新都不是無病呻吟的改變,而是爲了解決問題,Fiber 的引入主要是爲了解決在網頁裏面用戶和網頁應用進行交互的問題:github

  • 在 react16 以前的版本中,組建的渲染是同步的動做,若是組件包含不少層子組件,渲染時間比較長,在組件渲染的過程當中又沒法被打斷,會致使這期間用戶沒法與網頁進行交互。
  • 全部的任務都是按照前後順序,沒有優先級可言,這樣就會致使優先級比較高的任務沒法優先被執行。

react15 存在的問題

咱們用下面的一個案例來描述 react15 存在的問題: 在一個頁面元素不少,且須要頻繁刷新的狀況下,react15 會出現失幀的狀況,以下圖: demo地址:claudiopro.github.io/react-fiber…web

分析出現這個問題的緣由:

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

react 的解題思路:

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

react16 的新答卷

react 框架內部的運做能夠分爲三層:

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

這裏改動最大的當屬 Reconciler 層了,爲了和以前的作區別,咱們有了以下稱呼的約定:框架

  • Stack Reconciler(react15 的Reconciler)
  • Fiber Reconciler(react16 的Reconciler)

Stack Reconciler 的運行過程

Stack Reconciler 運做的過程是不能被打斷的,必須一條道走到黑:異步

Fiber Reconciler 的運行過程

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

Fiber Reconciler 是如何實現的

任務優先級佈局

爲了達到這種效果,須要有一個調度器(schedular)來根據任務的優先級進行任務的分配。優先級高的任務(如鍵盤輸入)能夠打斷優先級低的任務的執行,從而更快的生效。任務的優先級分爲六種:

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

Fiber Reconciler 的執行過程

在react的生命週期中,以 render 爲界限,分爲兩個階段,分別爲:

  • render phase: 這一階段的生命週期是能夠被打斷的,每隔一段時間就會跳出當前進程,去看下是否有優先級更高的任務在等待執行
  • commit phase: 這一階段的生命週期是不能夠被打斷的,react 會將全部的變動一次性更新到 DOM 上

瞭解下 render phase 的機制

render phase 這個階段所作的事很是重要,所以咱們須要瞭解下 render phase 的機制。

  • 不被打斷: 若是不被打斷,那麼在 render phase 階段執行完會直接進入 render 函數,構建真實的 virtualDomTree
  • 被打斷: 若是組件在 render phase 過程當中被打斷,react 會放棄當前組件幹到一半的事情,去作更高優先級的任務。當高優先級任務執行完以後,react 經過 callback 回到以前渲染到一半的組件,從頭開始渲染。(看起來放棄已經渲染完的生命週期,會有點不合理,反而會增長渲染時長,可是react確實是這麼幹的)

這種方式的問題

由於 render phase 階段會被打斷的特性,因此 render phase 階段的生命週期函數可能會被執行屢次。所以,咱們須要保證 render phase 階段的生命週期函數是沒有反作用的純函數,確保每次執行都是同樣的輸出結果。

寫在最後

Facebook比較慎重,16的第一個版本並無啓用這個功能,防止改變太大,影響到別的開發人員的正常工做。在 react16 晚一些的版本中會給出一個選項能夠開啓 fiber,在默認狀況下是關閉的。

另外,facebook 在 react16 增長 Fiber 結構,其實並非爲了減小組件的渲染時間,事實上也並不會減小,反而會增長。解決的是使得一些更高優先級的任務可以優先執行,提升用戶的體驗,從用戶的角度不會感受到卡頓。

最後,還有一些問題並無獲得很好地解釋,好比飢餓問題,即若是優先級高的任務一直存在,那麼優先級低的任務將一直沒有辦法執行。咱們期待 react 能給出優秀的解決方案。

相關文章
相關標籤/搜索