React Fiber源碼分析 第一篇
React Fiber源碼分析 第二篇(同步模式)
React Fiber源碼分析 第三篇(異步狀態)
React Fiber源碼分析 第四篇(概括總結)javascript
React Fiber是React在V16版本中的大更新,利用了閒餘時間看了一些源碼,作個小記錄~java
實際上此次更新對於咱們來講影響並不大,只是幾個生命週期改變了(React在版本中的更新簡直作到了像一門語言同樣,完美的兼容老版本,底層算法的大重構對於開發者來講徹底透明),新引入的兩個生命週期函數 getDerivedStateFromProps,getSnapshotBeforeUpdate 以及在將來 v17.0 版本中即將被移除的三個生命週期函數componentWillMount,componentWillReeiveProps,componentWillUpdate,目前版本並不會影響原生命週期的使用,但不能和新的生命週期一塊兒使用,也會被標記爲不安全,下圖爲目前React的流程圖算法
其餘的幾乎沒有任何影響,咱們仍是照常的寫着原來的代碼,而後咱們就感受到網頁性能更高了一些。瀏覽器
要回答這個問題,須要回頭看javascript是單線程的知識點。安全
單線程一次只能作一件事, 在原來的React中, 若是一次更新的時間比較長,那麼用戶就會感受到卡頓,也就是丟幀了。bash
打個比方, 假如我如今要更新1000個組件(往大了說),每一個組件平均花時間1ms,那麼在1s內,瀏覽器的整個線程都被阻塞了,這時候用戶在input上的任何操做都不會有反應,等到更新完畢,界面上突的一下就顯示了原來用戶的輸入,這個體驗是很是差的。這裏借用官方一張圖, Fiber以前的版本就是這樣,調用棧很是深
網絡
那麼Fiber,如今是怎麼作呢?數據結構
Fiber其實是把一次更新拆成一個個的單元任務,每次作完一個單元任務後,就詢問是否有更高的優先級任務,有就去執行,回頭再來幹這件事,如圖
異步
那麼就明白了,Fiber是一個任務調和器!, 一樣,咱們根據這個來分析Fiber具體作了什麼函數
首先,要作到這樣的效果,那麼就須要有如下的功能:
在React中,不管是state仍是props的更新, 最後都操做在JSX的標籤上
利用這種自然友好的表達,直接把每個標籤當成一個任務分片如:div、p一、p二、span都是一個任務分片
<div>
<p>p1</p>
<p>
<span>p2</span>
</p>
</div>
複製代碼
固然, 還要從標籤轉換成VDOM,再轉成Fiber,纔是一個真正的任務片,如圖:
Fiber以前React是經過棧調度器進行遞歸更新,畢竟標籤化是自然嵌套的,對遞歸友好,可是遞歸很差break和continue
Fiber則是以鏈表的形式來進行逐步更新(深度優先遍歷算法),鏈表對break和continue友好Fiber節點擁有return, child, sibling三個屬性,分別對應父節點, 第一個孩子, 它右邊的兄弟,
React內部維護一個任務鏈表,每次某個任務結束後都會刪除已完成的任務並繼續執行其餘可執行的任務,每一個任務都有一個finishedWork屬性,若是該屬性不爲null,則說明更新完畢,只差commit render階段
這個主要依賴於fiber中的兩個屬性expirationTime和childExpirationTime,當某個fiber被執行完畢後,會把expirationTime設爲NoWork,即被打斷後能夠經過該屬性判斷任務碎片是否 須要執行
this.expirationTime = NoWork // 任務優先級
this.childExpirationTime = NoWork // 子任務片的優先級
複製代碼
每次更新都不會對fiber直接操做,而是克隆一個做爲alternater屬性
更新隊列, 存放更新的信息
收集更新信息,生成真實DOM
每一個Root任務\更新任務\fiber都具備expirationTime屬性,該屬性即爲優先級expirationTime越小,優先級越高,同步模式下該值爲0, 每一個層級的任務都是以鏈表的形式存在
這時候就是requestIdleCallback這個API的騷操做了, 這個API是幹嗎的呢?
window.requestIdleCallback()會在瀏覽器空閒時期依次調用函數, 這就可讓開發者在主事件循環中執行後臺或低優先級的任務,並且不會對像動畫和用戶交互這樣延遲觸發並且關鍵的事件產生影響。函數通常會按先進先調用的順序執行,除非函數在瀏覽器調用它以前就到了它的超時時間。
也就是說React實際上利用這個API在瀏覽器空閒期執行任務, 而這個API的回調有個參數deadline , 當你超時的時候,不管是否是在空閒期都會執行該任務, 這也就解釋了爲何React採用時間來作優先級
不過實際上, React並無在版本中使用了這個API,而是經過requestAnimationFrame來hack,強行設置每一幀的到期時間爲requestAnimationFrame回調函數的參數加上33ms
var animationTick = function (rafTime) {
isAnimationFrameScheduled = false;
...
...
// 每幀到期時間爲33ms
frameDeadline = rafTime + 33
if (!isIdleScheduled) {
isIdleScheduled = true;
window.postMessage(messageKey, '*');
}
};
複製代碼
固然了, 分優先級是有一個沒法避免的問題, 那就是當有無數的優先級更高的任務插進來, 就會造成飢餓現象,原有的任務會一直得不到機會執行
React Fiber實際上就是一個任務調和器,它作到了將每一次更新切分紅任務分片,從而擁有了可中斷且有優先級的進行其餘任務的功能。 在分析的過程當中,發現了React的源碼中使用了不少鏈式結構, 回調鏈,任務鏈等,這個主要是爲了增刪時性能比較高