摘要:為什么網(wǎng)頁(yè)性能會(huì)變高要回答這個(gè)問(wèn)題,需要回頭看是單線程的知識(shí)點(diǎn)。在分析的過(guò)程中,發(fā)現(xiàn)了的源碼中使用了很多鏈?zhǔn)浇Y(jié)構(gòu),回調(diào)鏈,任務(wù)鏈等,這個(gè)主要是為了增刪時(shí)性能比較高
系列文章
React Fiber源碼分析 第一篇
React Fiber源碼分析 第二篇(同步模式)
React Fiber源碼分析 第三篇(異步狀態(tài))
React Fiber源碼分析 第四篇(歸納總結(jié))
React Fiber是React在V16版本中的大更新,利用了閑余時(shí)間看了一些源碼,做個(gè)小記錄~
什么是Fiber 從開發(fā)者角度來(lái)看實(shí)際上這次更新對(duì)于我們來(lái)說(shuō)影響并不大,只是幾個(gè)生命周期改變了(React在版本中的更新簡(jiǎn)直做到了像一門語(yǔ)言一樣,完美的兼容老版本,底層算法的大重構(gòu)對(duì)于開發(fā)者來(lái)說(shuō)完全透明),新引入的兩個(gè)生命周期函數(shù) getDerivedStateFromProps,getSnapshotBeforeUpdate 以及在未來(lái) v17.0 版本中即將被移除的三個(gè)生命周期函數(shù)componentWillMount,componentWillReeiveProps,componentWillUpdate,目前版本并不會(huì)影響原生命周期的使用,但不能和新的生命周期一起使用,也會(huì)被標(biāo)記為不安全,下圖為目前React的流程圖
其他的幾乎沒有任何影響,我們還是照常的寫著原來(lái)的代碼,然后我們就感覺到網(wǎng)頁(yè)性能更高了一些。
為什么網(wǎng)頁(yè)性能會(huì)變高要回答這個(gè)問(wèn)題,需要回頭看javascript是單線程的知識(shí)點(diǎn)。
單線程一次只能做一件事, 在原來(lái)的React中, 如果一次更新的時(shí)間比較長(zhǎng),那么用戶就會(huì)感覺到卡頓,也就是丟幀了。
打個(gè)比方, 假如我現(xiàn)在要更新1000個(gè)組件(往大了說(shuō)),每個(gè)組件平均花時(shí)間1ms,那么在1s內(nèi),瀏覽器的整個(gè)線程都被阻塞了,這時(shí)候用戶在input上的任何操作都不會(huì)有反應(yīng),等到更新完畢,界面上突的一下就顯示了原來(lái)用戶的輸入,這個(gè)體驗(yàn)是非常差的。這里借用官方一張圖, Fiber之前的版本就是這樣,調(diào)用棧非常深
那么Fiber,現(xiàn)在是怎么做呢?
Fiber實(shí)際上是把一次更新拆成一個(gè)個(gè)的單元任務(wù),每次做完一個(gè)單元任務(wù)后,就詢問(wèn)是否有更高的優(yōu)先級(jí)任務(wù),有就去執(zhí)行,回頭再來(lái)干這件事,如圖
那么就明白了,F(xiàn)iber是一個(gè)任務(wù)調(diào)和器!, 同樣,我們根據(jù)這個(gè)來(lái)分析Fiber具體做了什么
Fiber具體做了什么首先,要做到這樣的效果,那么就需要有以下的功能:
任務(wù)可分片 (拆分任務(wù))
任務(wù)可中斷 (執(zhí)行另一個(gè)任務(wù)后, 可以回頭繼續(xù)執(zhí)行未完成的任務(wù))
具備優(yōu)先級(jí) (哪個(gè)任務(wù)先執(zhí)行)
任務(wù)可分片在React中,無(wú)論是state還是props的更新, 最后都操作在JSX的標(biāo)簽上
利用這種天然友好的表達(dá),直接把每一個(gè)標(biāo)簽當(dāng)成一個(gè)任務(wù)分片如:div、p1、p2、span都是一個(gè)任務(wù)分片
p1
p2
當(dāng)然, 還要從標(biāo)簽轉(zhuǎn)換成VDOM,再轉(zhuǎn)成Fiber,才是一個(gè)真正的任務(wù)片,如圖:
Fiber之前React是通過(guò)棧調(diào)度器進(jìn)行遞歸更新,畢竟標(biāo)簽化是天然嵌套的,對(duì)遞歸友好,但是遞歸不好break和continue
Fiber則是以鏈表的形式來(lái)進(jìn)行逐步更新(深度優(yōu)先遍歷算法),鏈表對(duì)break和continue友好Fiber節(jié)點(diǎn)擁有return, child, sibling三個(gè)屬性,分別對(duì)應(yīng)父節(jié)點(diǎn), 第一個(gè)孩子, 它右邊的兄弟,
(圖來(lái)自網(wǎng)絡(luò),侵刪)
React內(nèi)部維護(hù)一個(gè)任務(wù)鏈表,每次某個(gè)任務(wù)結(jié)束后都會(huì)刪除已完成的任務(wù)并繼續(xù)執(zhí)行其他可執(zhí)行的任務(wù),每個(gè)任務(wù)都有一個(gè)finishedWork屬性,如果該屬性不為null,則說(shuō)明更新完畢,只差commit render階段
這個(gè)主要依賴于fiber中的兩個(gè)屬性expirationTime和childExpirationTime,當(dāng)某個(gè)fiber被執(zhí)行完畢后,會(huì)把expirationTime設(shè)為NoWork,即被打斷后可以通過(guò)該屬性判斷任務(wù)碎片是否
需要執(zhí)行
this.expirationTime = NoWork // 任務(wù)優(yōu)先級(jí) this.childExpirationTime = NoWork // 子任務(wù)片的優(yōu)先級(jí)
通過(guò)深度遍歷搜索算法對(duì)每一個(gè)fiber即任務(wù)碎片進(jìn)行更新
每一個(gè)任務(wù)碎片完成后會(huì)將expirationTime設(shè)為NoWork
假設(shè)此時(shí)有更高優(yōu)先級(jí)的任務(wù),則執(zhí)行更高優(yōu)先級(jí)任務(wù)
任務(wù)執(zhí)行完成后,會(huì)從任務(wù)列表中剔除,并繼續(xù)執(zhí)行其他未完成且可以執(zhí)行的任務(wù)。
回到被打斷任務(wù),可以通過(guò)任務(wù)的finishWork屬性判斷是否需要執(zhí)行更新
根據(jù)任務(wù)碎片的expirationTime判斷是否需要執(zhí)行更新
每次更新都不會(huì)對(duì)fiber直接操作,而是克隆一個(gè)作為alternater屬性
更新隊(duì)列, 存放更新的信息
收集更新信息,生成真實(shí)DOM
具備優(yōu)先級(jí)每個(gè)Root任務(wù)更新任務(wù)fiber都具有expirationTime屬性,該屬性即為優(yōu)先級(jí)expirationTime越小,優(yōu)先級(jí)越高,同步模式下該值為0, 每個(gè)層級(jí)的任務(wù)都是以鏈表的形式存在
這時(shí)候就是requestIdleCallback這個(gè)API的騷操作了, 這個(gè)API是干嘛的呢?
window.requestIdleCallback()會(huì)在瀏覽器空閑時(shí)期依次調(diào)用函數(shù), 這就可以讓開發(fā)者在主事件循環(huán)中執(zhí)行后臺(tái)或低優(yōu)先級(jí)的任務(wù),而且不會(huì)對(duì)像動(dòng)畫和用戶交互這樣延遲觸發(fā)而且關(guān)鍵的事件產(chǎn)生影響。函數(shù)一般會(huì)按先進(jìn)先調(diào)用的順序執(zhí)行,除非函數(shù)在瀏覽器調(diào)用它之前就到了它的超時(shí)時(shí)間。
也就是說(shuō)React實(shí)際上利用這個(gè)API在瀏覽器空閑期執(zhí)行任務(wù), 而這個(gè)API的回調(diào)有個(gè)參數(shù)deadline , 當(dāng)你超時(shí)的時(shí)候,無(wú)論是不是在空閑期都會(huì)執(zhí)行該任務(wù), 這也就解釋了為什么React采用時(shí)間來(lái)做優(yōu)先級(jí)
不過(guò)實(shí)際上, React并沒有在版本中使用了這個(gè)API,而是通過(guò)requestAnimationFrame來(lái)hack,強(qiáng)行設(shè)置每一幀的到期時(shí)間為requestAnimationFrame回調(diào)函數(shù)的參數(shù)加上33ms
var animationTick = function (rafTime) { isAnimationFrameScheduled = false; ... ... // 每幀到期時(shí)間為33ms frameDeadline = rafTime + 33 if (!isIdleScheduled) { isIdleScheduled = true; window.postMessage(messageKey, "*"); } };
當(dāng)然了, 分優(yōu)先級(jí)是有一個(gè)無(wú)法避免的問(wèn)題, 那就是當(dāng)有無(wú)數(shù)的優(yōu)先級(jí)更高的任務(wù)插進(jìn)來(lái), 就會(huì)形成饑餓現(xiàn)象,原有的任務(wù)會(huì)一直得不到機(jī)會(huì)執(zhí)行
總結(jié)React Fiber實(shí)際上就是一個(gè)任務(wù)調(diào)和器,它做到了將每一次更新切分成任務(wù)分片,從而擁有了可中斷且有優(yōu)先級(jí)的進(jìn)行其他任務(wù)的功能。
在分析的過(guò)程中,發(fā)現(xiàn)了React的源碼中使用了很多鏈?zhǔn)浇Y(jié)構(gòu), 回調(diào)鏈,任務(wù)鏈等,這個(gè)主要是為了增刪時(shí)性能比較高
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/101474.html
摘要:系列文章源碼分析第一篇源碼分析第二篇同步模式源碼分析第三篇異步狀態(tài)源碼分析第四篇?dú)w納總結(jié)前言是在版本中的大更新,利用了閑余時(shí)間看了一些源碼,做個(gè)小記錄流程圖源碼分析先由編譯,調(diào)用,入?yún)椋蛴〕鰜?lái)可以看到,,分別代表著元素原生元素,回調(diào)函數(shù) 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(...
摘要:系列文章源碼分析第一篇源碼分析第二篇同步模式源碼分析第三篇異步狀態(tài)源碼分析第四篇?dú)w納總結(jié)前言是在版本中的大更新,利用了閑余時(shí)間看了一些源碼,做個(gè)小記錄流程圖源碼分析調(diào)用時(shí),會(huì)調(diào)用的方法,同時(shí)將新的作為參數(shù)傳進(jìn)會(huì)先調(diào)用獲取一個(gè)維護(hù)兩個(gè)時(shí)間一個(gè) 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(...
摘要:函數(shù)主要執(zhí)行兩個(gè)操作,一個(gè)是判斷當(dāng)前是否還有任務(wù),如果沒有,則從鏈中移除。 系列文章 React Fiber源碼分析 第一篇 React Fiber源碼分析 第二篇(同步模式) React Fiber源碼分析 第三篇(異步狀態(tài)) React Fiber源碼分析 第四篇(歸納總結(jié)) 前言 React Fiber是React在V16版本中的大更新,利用了閑余時(shí)間看了一些源碼,做個(gè)小記...
摘要:如果你的運(yùn)行緩慢,你可以考慮是否能優(yōu)化請(qǐng)求,減少對(duì)的操作,盡量少的操,或者犧牲其它的來(lái)?yè)Q取性能。在認(rèn)識(shí)描述這些核心元素的過(guò)程中,我們也會(huì)分享一些當(dāng)我們構(gòu)建的時(shí)候遵守的一些經(jīng)驗(yàn)規(guī)則,一個(gè)應(yīng)用應(yīng)該保持健壯和高性能來(lái)維持競(jìng)爭(zhēng)力。 一個(gè)開源的前端錯(cuò)誤收集工具 frontend-tracker,你值得收藏~ 蒲公英團(tuán)隊(duì)最近開發(fā)了一款前端錯(cuò)誤收集工具,名叫 frontend-tracker ,這款...
摘要:大家可以看到是構(gòu)造函數(shù)構(gòu)造出來(lái)的,并且內(nèi)部有一個(gè)對(duì)象,這個(gè)對(duì)象是本文接下來(lái)要重點(diǎn)介紹的對(duì)象,接下來(lái)我們就來(lái)一窺究竟吧。在構(gòu)造函數(shù)內(nèi)部就進(jìn)行了一步操作,那就是創(chuàng)建了一個(gè)對(duì)象,并掛載到了上。下一篇文章還是流程相關(guān)的內(nèi)容。這是我的剖析 React 源碼的第二篇文章,如果你沒有閱讀過(guò)之前的文章,請(qǐng)務(wù)必先閱讀一下 第一篇文章 中提到的一些注意事項(xiàng),能幫助你更好地閱讀源碼。 文章相關(guān)資料 React ...
閱讀 3079·2023-04-25 18:54
閱讀 2598·2021-11-02 14:40
閱讀 3193·2021-09-23 11:58
閱讀 2438·2019-08-30 13:50
閱讀 1243·2019-08-29 12:46
閱讀 3129·2019-08-28 17:51
閱讀 687·2019-08-26 11:47
閱讀 907·2019-08-23 16:17