一篇帶你了解 React Fiber 是什么?
大家好,我是前端西瓜哥。
為了提高 React 的性能,React 團隊在開發(fā) React 16 時做了底層的重構,引入了 React Fiber 的概念。
React Fiber 是什么?
Fiber,本意為 “纖維”,在計算機世界中則是 ”纖程“ 的意思。纖程可以看作是協(xié)程的一種,是一種任務調度方式。
JavaScript 是單線程的,有一個 event loop 的概念,它有一個有優(yōu)先級的任務隊列,只能按順序執(zhí)行一個任務,是不支持多個任務同時執(zhí)行的。
這種設計的好處就是不用考慮多線程導致的順序問題,并為此做一些加鎖的額外邏輯,確保執(zhí)行順序符合預期。但也因為無法使用并行能力,在 CPU 密集的場景會有性能問題, 比如一個任務耗時過長會導致其他的任務,導致用戶的交互響應發(fā)生延遲。
?React 的組件更新是 CPU 密集的操作,因為它要做對比新舊虛擬 DOM 樹的操作(diff,React 中 Reconcilation 負責),找出需要更新的內容(patch),通過打補丁的方式更新真實 DOM 樹(React 中 Renderer 負責)。當要對比的組件樹非常多時,就會發(fā)生大量的新舊節(jié)點對比,CPU 花費時間龐大,當耗時大大超過 16.6ms(一秒 60 幀的基準) 時,用戶會感覺到明顯的卡頓。
這一系列操作是通過遞歸的方式實現(xiàn)的,是 同步且不可中斷 的。因為一旦中斷,調用棧就會被銷毀,中間的狀態(tài)就丟失了。這種基于調用棧的實現(xiàn),我們稱為 Stack Reconcilation。
React 16 的一個重點工作就是優(yōu)化更新組件時大量的 CPU 計算,最后選擇了使用 “時間分片” 的方案,就是將原本要一次性做的工作,拆分成一個個異步任務,在瀏覽器空閑的時間時執(zhí)行。這種新的架構稱為 Fiber Reconcilation。
在 React 中,F(xiàn)iber 模擬之前的遞歸調用,具體通過鏈表的方式去模擬函數(shù)的調用棧,這樣就可以做到中斷調用,將一個大的更新任務,拆分成小的任務,并設置優(yōu)先級,在瀏覽器空閑的時異步執(zhí)行。
FiberNode
前面我們說到使用了鏈表的遍歷來模擬遞歸棧調用,其中鏈表的節(jié)點 React 用 FiberNode 表示。
FiberNode 其實就是虛擬 DOM,它記錄了:
- 節(jié)點相關類型,比如 tag 表示組件類型、type 表示元素類型等。
- 節(jié)點的指向。
- 副作用相關的屬性。
- lanes 是關于調度優(yōu)先級的。
Fiber 通過 return 指向父 Fiber,child 指向子 Fiber 的首位、sibling 指向下一個兄弟節(jié)點。通過它們我們其實就能拿到一個完整的結構樹。
對于:
形成的 Fiber 樹為:
其中弧線為調用順序。紫色為 beginWork、粉色為 completeWork。beginWork 是 “遞” 的過程,而 comleteWork 則是 “歸” 的過程。
為什么不用 generator 或 async/await?
generator 和 async/await 也可以做到在函數(shù)中間暫停函數(shù)執(zhí)行的邏輯,將執(zhí)行讓出去,能做到將同步變成異步。
但 React 沒有選擇它們,這是因為:
- 具有傳染性,比如一個函數(shù)用了 async,調用它的函數(shù)就要加上 async,有語法開銷,此外也會有性能上的額外開銷。
- 無法在 generator 和 async/await 中恢復一些中間狀態(tài)。
具體見官方的 github issue 討論:
https://github.com/facebook/react/issues/7942#issuecomment-254987818。
Scheduler
做了時間分片,拆分了多個任務,React 就可以以此為基石,給任務設置優(yōu)先級。
React 實現(xiàn)了一個 Scheduler(調度器)來實現(xiàn)任務調度執(zhí)行,并單獨抽離為一個單獨的包,它會在瀏覽器有空閑的時候執(zhí)行。其實瀏覽器也提供了一個 requestIdleCallback 的 API,支持這個能力,但兼容性實在不好,React 還是自己實現(xiàn)了一套。
這個 Scheduler 支持優(yōu)先級,底層使用了 小頂堆,確保能高效拿到最快要過期的任務,然后執(zhí)行它。
小頂堆,其實就是優(yōu)先級隊列。小頂堆在結構上是一個完全二叉樹,但能保證每次從堆頂取出元素時,是最小的元素。
任務的 優(yōu)先級 分為幾種:
- NoPriority:無優(yōu)先級。
- ImmediatePriority:立即執(zhí)行。
- UserBlockingPriority:用戶阻塞優(yōu)先級,不執(zhí)行可能會導致用戶交互阻塞。
- NormalPriority:普通優(yōu)先級。
- LowPriority:低優(yōu)先級。
- IdlePriority:空閑優(yōu)先級。
React 自身也有優(yōu)先級,叫做 Lane,兩者是不同的。
結尾
React 的架構過于宏大,今天先隨便說一點吧。
總的來說,React Fiber 是在 React 16 中引入的新的架構,將原本同步不可中斷的更新,變成異步可中斷更新,將原本一個耗時的大任務做了時間分片,拆分成一個個小任務,在瀏覽器空閑的時間執(zhí)行。此外添加優(yōu)先級的概念,將一些重要的任務先執(zhí)行,比如一些用戶交互的響應函數(shù)。
一切為了更好的用戶體驗。