React Fiber中面試官最關(guān)心的技術(shù)話題
引言
關(guān)于 React Fiber 出來也有幾年了,可最近面試多了才發(fā)現(xiàn),還是有很多人一知半解,所以本文梳理了一下有關(guān) Fiber、以及這個(gè)話題環(huán)環(huán)相扣,可以延伸的點(diǎn),給大家面試復(fù)習(xí)、查缺補(bǔ)漏,如果你是面試官也可直接拿去提問
React Fiber
說一下 React Fiber
是指時(shí)間分片嘛,可以把渲染工作切片,優(yōu)化渲染性能的。
因?yàn)?nbsp;React 是函數(shù)式編程嘛,單向數(shù)據(jù)流,需要手動(dòng) setState 來更新,所以當(dāng)數(shù)據(jù)改變時(shí)會(huì)默認(rèn)全部重新渲染整個(gè)組件樹,而構(gòu)建虛擬 DOM 則是采用同步遞歸的方式,如果組件很復(fù)雜且嵌套深,那么這個(gè)構(gòu)建虛擬 DOM 的過程就需要很多時(shí)間,而這種任務(wù)默認(rèn)要執(zhí)行完才會(huì)把控制權(quán)交給瀏覽器,一旦執(zhí)行時(shí)間很長,可能就會(huì)地把瀏覽器卡死。
雖然可以用 pureComponent/shouldComponentUpdate/useMemo/useCallback 等方法來進(jìn)行控制部分更新或緩存,但也是治標(biāo)不治本。而 fiber 是可以使渲染過程被中斷,可以把控制權(quán)先交還給瀏覽器,讓位高優(yōu)先級(jí)的任務(wù),等瀏覽器空閑時(shí)再恢復(fù)渲染,這樣就不會(huì)顯得卡頓,而是一幀一幀有規(guī)律的執(zhí)行任務(wù),看起來雖然有點(diǎn)慢,但是一直在慢慢執(zhí)行的
就像一個(gè)房子,組件層級(jí)就像房子的樓層,但是每一層房子頂都破了很多個(gè)洞的,那幾個(gè)方法就像一片瓦,都能蓋一個(gè)小洞,但稍不注意少蓋一個(gè)房間就會(huì)漏水,房間里的人(用戶)就能感知到(卡),而 fiber 就像在破洞下面放了根水管直接通到屋外,雖然看起來水管彎彎繞繞的,水落地的時(shí)間慢了(執(zhí)行時(shí)間長了),但房間里的人,就感覺不到漏水了(屋子還是個(gè)破屋子)
實(shí)在是因?yàn)?nbsp;React 沒辦法在編譯階段優(yōu)化更多了,所以采用這種方式來彌補(bǔ)傷害
React Fiber 是怎么實(shí)現(xiàn)的
主要是通過兩個(gè)原生的 API 來實(shí)現(xiàn)的 requestAnimationFrame 和 requestIdleCallback
顯示器每秒 60 幀我們看著才不會(huì)感覺到卡嘛,比如動(dòng)畫的時(shí)候,一幀的時(shí)間內(nèi)布局和繪制結(jié)束,還有剩余時(shí)間,JS 就會(huì)拿到主線程使用權(quán),如果 JS 某個(gè)任務(wù)執(zhí)行過長,動(dòng)畫下一幀開始時(shí) JS 還沒有執(zhí)行完,就會(huì)導(dǎo)致掉幀,出現(xiàn)卡頓。
所以就通過把 JS 任務(wù)分成更小的任務(wù)塊,分到每一幀上的方式,一幀時(shí)間到先暫停 JS 執(zhí)行,然后下一幀繪制任完成再把主線程交給 JS,在每一幀繪制之前調(diào)用 requestAnimationFrame;在每一幀空間階段,就是一幀動(dòng)畫任務(wù)完成,下一幀還沒到開始時(shí)間,這中間還有時(shí)間的話就調(diào)用 requetIdleCallback,執(zhí)行它里面的任務(wù)
其他方式能實(shí)現(xiàn)
函數(shù)式編程嘛,generator 就可以控制函數(shù)的運(yùn)行過程中斷和恢復(fù),就像這樣:
// 任務(wù)列表
const tasks = []
function * run () {
let task
while(task = task.shift()) {
// 如果有高優(yōu)先級(jí)的任務(wù)
if (hasHighPriorityTask()) {
// 中斷
yield
}
}
}
// 中斷后恢復(fù)
const iterator = run()
// 這樣就恢復(fù)了
iterator.next()為什么不直接用 generator
這個(gè)問題 React 開發(fā)者有在源碼里回答過:
- 一是因?yàn)?nbsp;
React是迭代的,而使用generator+yield的話需要把所有代碼都包裝成這個(gè)形式,非常麻煩,工作量很大 - 二是
generator內(nèi)部是有狀態(tài)的,比如一個(gè)函數(shù)里有用到多個(gè)yield中斷,就像await一樣,有時(shí)候后面的會(huì)依賴前面的結(jié)果,可當(dāng)后面的執(zhí)行前,前面的又更新了,后面就無法拿到最新的值,這樣就不可控了
所以就自己實(shí)現(xiàn)一個(gè)完全可控的
怎么判斷當(dāng)前是否有高優(yōu)先級(jí)的任務(wù)
這個(gè)據(jù)我所知 JS 好像沒法判斷,而是按幀的時(shí)間,如果一幀內(nèi)任務(wù)還沒執(zhí)行完,就中斷當(dāng)前任務(wù),把控制權(quán)交給瀏覽器
瀏覽器什么時(shí)候才有空呢
每秒60幀算,每幀就是 1000/60 = 16.7ms 差不多,每幀任務(wù)執(zhí)行完會(huì)調(diào) requetIdleCallback(callback),并且在 callback 中會(huì)有一個(gè)參數(shù)告訴我們當(dāng)前幀還有多少時(shí)間給我們執(zhí)行任務(wù)
那瀏覽器一幀內(nèi)要做哪些事情
比如布局(layout)、繪制(paint)、JS 的執(zhí)行、處理用戶輸入事件、requestAnimation 調(diào)用等,如果在一幀內(nèi)處理完了這些剩余時(shí)間就用來執(zhí)行 requetIdleCallback,直到下一幀開始
- 如果瀏覽器很忙,一幀結(jié)束了還沒執(zhí)行怎么辦
requetIdleCallback(callback, options),還有第二個(gè)參數(shù),里面有個(gè) timeout 字段(毫秒),如果超過這個(gè)時(shí)間還沒有被執(zhí)行的話,那么下一幀就會(huì)強(qiáng)制執(zhí)行
- 就是說超時(shí)后一定會(huì)被執(zhí)行咯
也不一定,React 里有 5 個(gè)優(yōu)先級(jí)的等級(jí),高優(yōu)先級(jí)的會(huì)被優(yōu)先執(zhí)行,低優(yōu)先級(jí)的慢慢等下去,等級(jí)是這樣的
Immediate: 最高優(yōu)先級(jí),會(huì)馬上執(zhí)行的不能中斷UserBlocking: 這一般是用戶交互的結(jié)果,需要及時(shí)反饋Normal: 普通等級(jí)的,比如網(wǎng)絡(luò)請(qǐng)求等不需要用戶立即感受到變化的Low: 低優(yōu)先級(jí)的,這種任務(wù)可以延后,但最后始終是要執(zhí)行的Idle: 最低等級(jí)的任務(wù),可以被無限延遲的,比如console.log()
如果是相同優(yōu)先級(jí)的任務(wù),就會(huì)按推入任務(wù)隊(duì)列的順序來執(zhí)行
兼容性怎么樣
requetIdleCallback 兼容性是很差的,React 也是通過 messageChannel 模擬實(shí)現(xiàn)的 requetIdleCallback 的功能
除了 MessageChannel 還有沒有其他類似方法
還有一個(gè)功能類似的 BroadcastChannel
以上就是React Fiber中面試官最關(guān)心的技術(shù)話題的詳細(xì)內(nèi)容,更多關(guān)于React Fiber面試技術(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Jira 任務(wù)管理系統(tǒng)項(xiàng)目總結(jié)講解
這篇文章主要為大家介紹了Jira 任務(wù)管理系統(tǒng)項(xiàng)目總結(jié)講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
React引入css的幾種方式及應(yīng)用小結(jié)
這篇文章主要介紹了React引入css的幾種方式及應(yīng)用小結(jié),操作styled組件的樣式屬性,可在組件標(biāo)簽上定義屬性、也可以通過attrs定義屬性,本文通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-03-03
關(guān)于React Native報(bào)Cannot initialize a parameter of type''NSArra
這篇文章主要介紹了關(guān)于React Native報(bào)Cannot initialize a parameter of type'NSArray<id<RCTBridgeModule>>錯(cuò)誤,本文給大家分享解決方案,需要的朋友可以參考下2021-05-05
詳細(xì)談?wù)凴eact中setState是一個(gè)宏任務(wù)還是微任務(wù)
學(xué)過react的人都知道,setState在react里是一個(gè)很重要的方法,使用它可以更新我們數(shù)據(jù)的狀態(tài),下面這篇文章主要給大家介紹了關(guān)于React中setState是一個(gè)宏任務(wù)還是微任務(wù)的相關(guān)資料,需要的朋友可以參考下2021-09-09
react-native ListView下拉刷新上拉加載實(shí)現(xiàn)代碼
本篇文章主要介紹了react-native ListView下拉刷新上拉加載實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08

