React Fiber源碼分析 (介紹)


寫了分析源碼的文章后, 總覺得缺少了什么, 在這里補一個整體的總結,輸出個人的理解~

文章的系列標題為Fiber源碼分析, 那么什么是Fiber,官方給出的解釋是:

React Fiber是對核心算法的一次重新實現。

ummm, 這樣說實在是有點泛,詳細分析一下

 

 

先從開發者角度來看 

實際上這次更新對於我們來說影響並不大,只是幾個生命周期改變了,新引入的兩個生命周期函數 getDerivedStateFromPropsgetSnapshotBeforeUpdate 以及在未來 v17.0 版本中即將被移除的三個生命周期函數componentWillMount,componentWillReeiveProps,componentWillUpdate,目前版本並不會影響原生命周期的使用,但不能和新的生命周期一起使用,也會被標記為不安全,下圖為目前React的流程圖:

 

其他的幾乎沒有任何影響,我們還是照常的寫着原來的代碼,然后我們就感覺到網頁性能更高了一些。

 

 

為什么網頁性能會變高,Fiber做了什么?

要回答這個問題,需要回頭看javascript是單線程的知識點。

單線程一次只能做一件事, 在原來的React中, 如果一次更新的時間比較長,那么用戶就會感覺到卡頓,也就是丟幀了。

打個比方, 假如我現在要更新1000個組件(往大了說),每個組件平均花時間1ms,那么在1s內,瀏覽器的整個線程都被阻塞了,這時候用戶在input上的任何操作都不會有反應,等到更新完畢,界面上突的一下就顯示了原來用戶的輸入,這個體驗是非常差的。這里借用官方一張圖, Fiber之前的版本就是這樣,調用棧非常深

 

那么Fiber,現在是怎么做呢?

Fiber實際上是把一次更新拆成一個個的單元任務,每次做完一個單元任務后,就詢問是否有更高的優先級任務,有就去執行,回頭再來干這件事,如圖

那么就明白了,Fiber是一個任務調和器!, 同樣,我們根據這個來分析Fiber具體做了什么

 

 

Fiber具體做了什么

首先,要做到這樣的效果,那么就需要有以下的功能:

1.可分片  (拆分任務)

2.可中斷   (執行另一個任務后, 可以回頭繼續執行未完成的任務)

3.具備優先級 (哪個任務先執行)

 

想要做到拆分任務就需要任務可以分片,也就是React的Fiber,fiber即為一個分片任務,貼上數據結構:

可中斷即是使用了隊列的形式保存任務, 具體可以看源碼~

基本是一個fiber即為一個組件,而優先級即使用fiber的expirationTime屬性, expirationTime越小即優先級越高

function FiberNode(tag, pendingProps, key, mode) {
  // Instance
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // Fiber
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.firstContextDependency = null;

  this.mode = mode;

  // Effects
  this.effectTag = NoEffect;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  this.expirationTime = NoWork;
  this.childExpirationTime = NoWork;

  this.alternate = null;

} 

 

 

從數據結構上, 有幾個屬性值得說一下,

首先是官方注釋了Fiber的幾個屬性, 這幾個是非常重要的

原來的React更新任務是采用遞歸的形式, 那么現在如果任務想中斷, 在遞歸中是很難做處理的, 所以React改成了大循環的模式

修改了生命周期也是因為任務可中斷~

具體可以參考下面這篇文章

到目前為止(React 16.4),React的渲染機制遵循同步渲染: 
1) 首次渲染: willMount > render > didMount, 
2) props更新時: receiveProps > shouldUpdate > willUpdate > render > didUpdate 
3) state更新時: shouldUpdate > willUpdate > render > didUpdate 
3) 卸載時: willUnmount

期間每個周期函數各司其職,輸入輸出都是可預測,一路下來很順暢。

BUT 從React 17 開始,渲染機制將會發生顛覆性改變,這個新方式就是 Async Render。

首先,async render不是那種服務端渲染,比如發異步請求到后台返回newState甚至新的html,這里的async render還是限制在React作為一個View框架的View層本身。

通過進一步觀察可以發現,預廢棄的三個生命周期函數都發生在虛擬dom的構建期間,也就是render之前。在將來的React 17中,在dom真正render之前,React中的調度機制可能會不定期的去查看有沒有更高優先級的任務,如果有,就打斷當前的周期執行函數(哪怕已經執行了一半),等高優先級任務完成,再回來重新執行之前被打斷的周期函數。這種新機制對現存周期函數的影響就是它們的調用時機變的復雜而不可預測,這也就是為什么”UNSAFE”。
--------------------- 
作者:辰辰沉沉大辰沉 
來源:CSDN 
原文:https://blog.csdn.net/Napoleonxxx/article/details/81120854 

 

 

什么是大循環?

即執行某個fiber后, 會執行他的子元素, 如果沒有子元素, 則兄弟元素, 然后又回到父元素, 父兄弟元素...

而尋找元素則是根據其上面幾個屬性return(父元素),child(子元素),sibiling(兄弟元素)

假設有以下的代碼:

<div>
   <span1></span1>
   <p>
     <span2><span2>
   </p>
</div>

 

他的執行如圖

 

 

 

Fiber的優先級? 

 再下來, fiber又是怎么做到根據優先級執行任務時不會卡頓呢,如果任務很多, 無窮無盡, 那不是一樣會丟幀?

這時候就是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, '*');
    }
  };

 

當然了, 分優先級是有一個無法避免的問題, 那就是當有無數的優先級更高的任務插進來, 就會形成飢餓現象,原有的任務會一直得不到機會執行

 

后面還有一個打了注釋Effect標簽的幾個屬性,這幾個屬性主要是收集每次更新的結果, 並在最后一層層往上迭代, 最后由最高的節點收集, 並執行更新。

 

在分析的過程中,發現了React的源碼中使用了很多鏈式結構, 回調鏈,任務鏈等, 這個主要是為了增刪時性能比較高

 

最后總結一下:

React Fiber實際上就是一個任務調和器,它做到了將每一次更新切分成任務分片,從而擁有了可中斷且有優先級的進行其他任務的功能。

 如果想看源碼, 可以參考本系列的另外三篇文章

React Fiber源碼分析 第一篇

React Fiber源碼分析 第二篇(同步模式)

React Fiber源碼分析 第三篇(異步狀態)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM