您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“怎么理解的React Fiber”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“怎么理解的React Fiber”吧!
在早期的 React 版本中,也就是 React16.8 版本之前。
大量的同步計算任務
阻塞了瀏覽器的UI渲染。默認情況下,JS運算
、頁面布局
和頁面繪制渲染
都是運行在瀏覽器的主線程
當中,他們之間是互斥
的關(guān)系。
如果 JS 運算持續(xù)占用主線程,頁面就沒法得到及時的更新,當我們調(diào)用setState
更新頁面的時候,React 會遍歷應用的所有節(jié)點,與老的 dom 節(jié)點進行 diff 算法的對比,最小代價更新頁面,即使
這樣,整個過程也是一氣呵成,不能被打斷
的,如果頁面元素很多,整個過程占用的時間就可能超過16毫秒,出現(xiàn)掉幀的現(xiàn)象。
針對這一現(xiàn)象,React 團隊從框架層面對 web 頁面的運行機制做了優(yōu)化,此后,Fiber
誕生了。
說到16ms,我們來看這樣的一個概念
目前大多數(shù)設備的屏幕刷新率為60次/秒
瀏覽器的渲染動畫或頁面的每一幀的速率也需要跟設備屏幕的刷新率保持一致。
頁面是一幀一幀繪制出來的,當每秒繪制的幀數(shù)(FPS)達到60時,頁面是流暢的,小于這個值時,用戶會感覺到卡頓。
每個幀的預算時間是16.66毫秒(1秒/60)
1s 60幀,所以我們書寫代碼時盡量不讓一幀的工作量超過16ms
解決主線程長時間被 JS 暈眩占用這一問題的基本思路,是將運算切割為多個步驟
,分批完成。也就是說在完成一部分任務之后, 將控制權(quán)交回
給瀏覽器,讓瀏覽器有時間再進行頁面的渲染。等瀏覽器忙完之后,再繼續(xù)之前React未完成的任務。
舊版 React 通過遞歸
的方式進行渲染,使用的是 JS 引擎自身的函數(shù)調(diào)用棧,它會一直執(zhí)行到??諡橹?/strong>。
而Fiber
實現(xiàn)了自己的組件調(diào)用棧,它以鏈表的形式遍歷組件樹,可以靈活地暫停、繼續(xù)和丟棄執(zhí)行的任務。實現(xiàn)的方式是使用了 瀏覽器的requestIdleCallback
這一 API。官方的解釋是這樣的:
window.requestIdleCallback()會在瀏覽器
空閑時期
依次調(diào)用函數(shù),這就可以讓開發(fā)者在主事件循環(huán)
中執(zhí)行后臺
或優(yōu)先級低
的任務,而且不會像對動畫和用戶交互這些延遲觸發(fā)產(chǎn)生關(guān)鍵的事件影響。函數(shù)一般會按先進先調(diào)用的順序執(zhí)行,除非函數(shù)在瀏覽器調(diào)用它之前就到了它的超時時間。
希望快速響應用戶,讓用戶覺得夠快,不能阻塞用戶的交互行為
requestIdleCallback 使開發(fā)者能夠在主事件循環(huán)
上執(zhí)行后臺和低優(yōu)先級
的工作,而不會影響延遲關(guān)鍵事件,例如動畫和輸入的響應
正常幀任務完成后沒超過16ms
,說明時間有賦予,此時就會執(zhí)行requestIdleCallback
里注冊的任務
Fiber 是一個執(zhí)行單元,每次執(zhí)行完一個執(zhí)行單元, React 就會檢查現(xiàn)在還剩多少時間,如果沒有時間就將控制權(quán)讓出去
React 目前的做法是使用鏈表, 每個 VirtualDOM 節(jié)點內(nèi)部表示為一個Fiber
,它可以用一個 JS 對象來表示:
const fiber = { stateNode, // 節(jié)點實例 child, // 子節(jié)點 sibling, // 兄弟節(jié)點 return, // 父節(jié)點 }
React 會遞歸比對
VirtualDOM樹,找出需要變動
的節(jié)點,然后同步更新它們。這個過程 React 稱為Reconcilation(協(xié)調(diào))
在Reconcilation期間,React 會一直占用
著瀏覽器資源,一則會導致用戶觸發(fā)的事件得不到響應, 二則會導致掉幀,用戶可能會感覺到卡頓
let root = { key: 'A1', children: [ { key: 'B1', children: [ { key: 'C1', children: [] }, { key: 'C2', children: [] } ] }, { key: 'B2', children: [] } ] } function walk(element) { doWork(element); element.children.forEach(walk); } function doWork(element) { console.log(element.key); } walk(root);
在 Fiber 出現(xiàn)之前, React 會不斷遞歸遍歷虛擬 DOM 節(jié)點,占用著瀏覽器資源,積極地浪費性能,造成卡頓現(xiàn)象,且協(xié)調(diào)階段是不能
被打斷的
。
Fiber 出現(xiàn)之后,通過某些 Fiber 調(diào)度策略合理分配 CPU 資源,讓自己的
協(xié)調(diào)階段變成可被終端
,適時
地讓 CPU(瀏覽器)執(zhí)行權(quán),提高了性能優(yōu)化。
每次渲染有兩個階段:Reconciliation(協(xié)調(diào)\render階段)和Commit(提交階段)
協(xié)調(diào)階段: 這個階段可以被中斷
, 通過Dom-Diff算法找出所有節(jié)點變更,例如節(jié)點新增
、刪除
、屬性變更
等等, 這些變更React 稱之為副作用
(Effect)
提交階段: 將上一個階段計算出來的需要處理的副作用(Effects)一次性執(zhí)行了。這個階段必須同步
執(zhí)行,不能被打斷
簡單理解的話
階段1:生成Fiber樹,得出需要更新
的節(jié)點信息
。(可打斷
)
階段2:將需要更新的節(jié)點一次性地批量更新
。(不可打斷
)
Fiber的協(xié)調(diào)階段,可以被優(yōu)先級較高的任務(如鍵盤輸入)打斷。
階段1可被打斷的特性,讓優(yōu)先級更高的任務先執(zhí)行
,從框架層面大大降低了頁面掉幀的概率。
Fiber Reconciliation(協(xié)調(diào)) 在階段一進行 Diff 計算的時候,會生成一棵 Fiber 樹
。這棵樹是在 Virtual DOM 樹
的基礎上增加額外的信息生成
來的,它本質(zhì)來說是一個鏈表。
Fiber 樹在首次渲染的時候會一次過生成。在后續(xù)
需要 Diff
的時候,會根據(jù)已有樹和最新 Virtual DOM 的信息,生成一棵新的樹。這顆新樹每生成一個新的節(jié)點,都會將控制權(quán)交回給主線程,去檢查有沒有優(yōu)先級更高的任務需要執(zhí)行。如果沒有,則繼續(xù)構(gòu)建樹的過程。
1.如果過程中有優(yōu)先級更高的任務需要進行,則 Fiber Reconciler 會丟棄正在生成的樹,在空閑的時候再重新執(zhí)行一遍。
2.在構(gòu)造 Fiber 樹的過程中,F(xiàn)iber Reconciler 會將需要更新的節(jié)點信息保存在Effect List
當中,在階段二執(zhí)行的時候,會批量更新相應的節(jié)點。
從頂點開始遍歷
如果有第一個兒子,先遍歷第一個兒子
如果沒有第一個兒子,標志著此節(jié)點遍歷完成
如果有弟弟遍歷弟弟
如果有沒有下一個弟弟,返回父節(jié)點標識完成父節(jié)點遍歷,如果有叔叔遍歷叔叔
沒有父節(jié)點遍歷結(jié)束
類比 Git 分支功能,從舊樹中 fork 出來一份,在新分支進行添加、刪除和更新操作,經(jīng)過測試后進行提交。
到此,相信大家對“怎么理解的React Fiber”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。