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