Fiber異步調和機制


緣起

React虛擬DOM的調和渲染可以簡單粗暴的遞歸,但是這個過程是同步的,如果需要處理的節點過多,可能會阻塞用戶輸入和動畫播放,布局等造成卡頓,Fiber是16.x引入的新特性,用處是將同步的調和變成異步的。

1. 簡單概述

它是React16之后新加入的一種異步VDom調和機制(當前默認沒有開啟這種異步模式),它把diff過程分解為多個可中斷的任務,像一個鏈表結構,任務之間可以被優先級更高的動畫,用戶事件打斷,讓瀏覽器運行得更瀏覽暢,不容易出現卡頓現象。

React15時,整個渲染邏輯是同步的,采用遞歸方式調用組件的生命周期函數和diff算法,React16之后把diff過程分解成多個小的可打斷的時間片,中間使用window.requestIdleCallback(chrome支持,其他瀏覽器需要模擬類似行為)這個實驗性的API解決任務調度的問題,讓瀏覽器在空閑時才計算diff並渲染。

requestIdleCallback來進行任務調度,它進行任務調度的思想是將任務拆分成多個小任務,requestIdleCallback里面不斷的把小任務拿出來執行,當所有任務都執行完或者超時了就結束本次執行,同時要注冊下次執行。

Fibe的思想總結來說,就是把一個大的diff任務分成很多小片,當分配給這個小片的時間用盡的時候,就把JS渲染進程的控制區交出去,讓瀏覽器去檢查有沒有優先級更高的任務,有就做這個新任務,沒有就繼續做原來的任務,這種方式被叫做異步渲染(Async Rendering)。

所以用原來VDom這個單一的大對象就不好做暫停繼續操作,所以引入了fiber這個新的可中斷可恢復的數據結構,那fiber結構大致是什么樣的?

  • fiber節點對象,有childsibing屬性,指向第一個子節點和相鄰的兄弟節點,return屬性指向其父節點,外形上構成了一個fiber樹,fiber樹本質上是一個鏈表結構。
  • 每個fiber(當前fiber可以稱為current)有一個屬性alternate,開始時指向一個自己的clone體,update的變化會先更新到alternate上,當更新完畢,alternate替換current
  • fiber tree是根據vDOM tree構造出來的,他們的樹結構一模一樣,只是節點攜帶的信息有差異,工作循環中,每次處理一個fiber,處理完可以中斷/掛起整個工作循環。

2. 基本原理

React 框架內部的運作可以分為 3 層

  • Virtual DOM 層,描述頁面長什么樣。
  • Reconciler 層,負責調用組件生命周期方法,進行 Diff 運算等。
  • Renderer 層,根據不同的平台,渲染出相應的頁面,比較常見的是 ReactDOM 和 ReactNative。

Fiber改動最大的當屬 Reconciler 層了,React 團隊也給它起了個新的名字,叫Fiber Reconciler(調和),之前叫Stack Reconfciler

Fiber Reconciler 在執行過程中,會分為 2 個階段

  • 階段一,生成 Fiber 樹,得出需要更新的節點信息。這一步是一個漸進的過程,可以被打斷。
  • 階段二,將需要更新的節點批量更新,這個過程不能被打斷。

階段一可被打斷,讓優先級更高的任務先執行,從框架層面大大降低了頁面掉幀的概率,當然被打斷時,fiber會記錄很多中間狀態,會記錄當前執行的位置,哪些節點已經完成diff計算了,下一次從哪里開始等。

在render函數中創建的React Element樹在第一次渲染的時候會創建一顆結構一模一樣的Fiber節點樹。不同的React Element類型對應不同的Fiber節點類型。一個React Element的工作就由它對應的Fiber節點來負責。

一個React Element可以對應不止一個Fiber,因為Fiber在update的時候,會從原來的Fiber(我們稱為current)clone出一個新的Fiber(我們稱為alternate)。兩個Fiber diff出的變化(side effect)記錄在alternate上,所以一個組件在更新時最多會有兩個Fiber與其對應,在更新結束后alternate會取代之前的current的成為新的current節點。

3. Fiber的基本規則

更新任務分成兩個階段,Reconciliation Phase和Commit Phase。

Reconciliation Phase的任務做的事情是,找出要做的更新工作(Diff Fiber Tree),就是一個計算階段,計算結果可以被緩存,也就可以被打斷;

Commmit Phase 需要提交所有更新並渲染,為了防止頁面抖動,被設置為不能被打斷。

4. 平時編程需要注意些什么?

componentWillMount, componentWillReceiveProps,shouldComponentUpdate(它沒啥大問題,不會產生副作用), componentWillUpdate 幾個生命周期方法不再安全,由於任務執行過程可以被打斷,這幾個生命周期可能會執行多次,如果它們包含副作用(比如aJax),會有意想不到的bug,React16.3團隊提供了一些新的周期函數,建議用新的周期函數做替待 when you have to do something with these life functions。

 static getDerivedStateFromProps(props, state) {
    // 代替componentWillReceiveProps
  }
 //新的靜態 getDerivedStateFromProps 生命周期方法在組件實例化之后以及重新渲染之前調用。它可以返回一個對象來更新 state,或者返回 null 來表示新的 props 不需要任何 state 的更新。
 getSnapshotBeforeUpdate(prevProps, prevState) // 它代替componentWillUpdate。建議如果使用以上方法,盡量用純函數,避免以后采坑

 


免責聲明!

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



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