摘要:架構(gòu)理解引用原文是核心算法正在進(jìn)行的重新實現(xiàn)。構(gòu)建的過程就是的過程,通過來調(diào)度執(zhí)行一組任務(wù),每完成一個任務(wù)后回來看看有沒有插隊的更緊急的,把時間控制權(quán)交還給主線程,直到下一次回調(diào)再繼續(xù)構(gòu)建。
React Fiber 架構(gòu)理解
引用原文:React Fiber ArchitectureReact Fiber is an ongoing reimplementation of React"s core algorithm. It is the culmination of over two years of research by the React team.
The goal of React Fiber is to increase its suitability for areas like animation, layout,and gestures. Its headline feature is incremental rendering: the ability to split rendering work into chunks and spread it out over multiple frames.
Other key features include the ability to pause, abort, or reuse work as new updates come in; the ability to assign priority to different types of updates; and new concurrency primitives.
React Fibre 是 React 核心算法正在進(jìn)行的重新實現(xiàn)。它是 React 團(tuán)隊兩年多的研究成果。
React Fiber 的目標(biāo)是提高其對動畫,布局和手勢等領(lǐng)域的適用性。它的主體特征是增量渲染:能夠?qū)秩竟ぷ鞣指畛蓧K,并將其分散到多個幀中。
其他主要功能包括在進(jìn)行更新時暫停,中止或重新使用工作的能力,為不同類型的更新分配優(yōu)先權(quán)的能力和新的并發(fā)原語。
React16之前組件的渲染邏輯先來看一下react組件渲染時經(jīng)歷的生命周期:
掛載階段:
constructor()
componentWillMount()
render()
componentDidMount()
更新階段:
componentWillReceiveProps()
shouldComponentUpdate()
componentWillUpdate()
render()
componentDidUpdate
卸載階段:
componentWillUnmount()
在之前的版本中,如果你實現(xiàn)一個很復(fù)雜的深度嵌套的復(fù)合組件,會出現(xiàn)下面的情況:
現(xiàn)有層級關(guān)系如下的四個組件:
組件渲染時調(diào)用的生命周期順序:
上圖展示的是A,B,C,D的掛載階段調(diào)用的生命周期渲染順序,可以看到從頂層組件開始調(diào)用各生命周期,一直向下,直至調(diào)用完最底層子組件的生命周期。然后再向上調(diào)用。
組件更新階段同理。
組件掛載之后,假如修改最上層組件的數(shù)據(jù)(state),組件更新時的調(diào)用棧:
如果這是一個很大,層級很深的組件,可以想像到,組件在渲染時,調(diào)用棧過長,再加上如果在期間進(jìn)行了各種復(fù)雜的操作,就可能導(dǎo)致長時間阻塞主線程,react渲染它需要幾十甚至幾百毫秒,這樣的話react就會一直占用瀏覽器主線程,任何其他的操作(包括用戶的點擊,鼠標(biāo)移動等操作)都無法執(zhí)行,帶來非常不好的用戶體驗。
React Fiber的出現(xiàn)React Fiber 就是為了解決上面的問題而生。
好似一個潛水員,當(dāng)它一頭扎進(jìn)水里,就要往最底層一直游,直到找到最底層的組件,然后他再上岸。在這期間,岸上發(fā)生的任何事,都不能對他進(jìn)行干擾,如果有更重要的事情需要他去做(如用戶操作),也必須得等他上岸。Fiber 本質(zhì)上是一個虛擬的堆棧幀,新的調(diào)度器會按照優(yōu)先級自由調(diào)度這些幀,從而將之前的同步渲染改成了異步渲染,在不影響體驗的情況下去分段計算更新。它讓潛水員會每隔一段時間就上岸,看是否有更重要的事情要做。
對于如何區(qū)別優(yōu)先級,React 有自己的一套邏輯。對于動畫這種實時性很高的東西,也就是 16 ms 必須渲染一次保證不卡頓的情況下,React 會每 16 ms(以內(nèi)) 暫停一下更新,返回來繼續(xù)渲染動畫。
React Fiber 架構(gòu) 調(diào)度拆分為小任務(wù)瀏覽器本身也不斷進(jìn)化中,隨著頁面由簡單的展示轉(zhuǎn)向WebAPP,它需要一些新能力來承載更多節(jié)點的展示與更新。
下面是一些自救措施:
requestAnimationFrame
requestIdleCallback
web worker
IntersectionObserver
react官方采用的是 requestIdleCallback,為了兼容所有平臺,facebook 多帶帶實現(xiàn)了其功能,作為一個獨立的 npm 包使用 react-schedule
其作用是會在瀏覽器空閑時期依次調(diào)用函數(shù), 這就可以在主事件循環(huán)中執(zhí)行后臺或低優(yōu)先級的任務(wù),而且不會對像動畫和用戶交互這樣延遲觸發(fā)而且關(guān)鍵的事件產(chǎn)生影響。函數(shù)一般會按先進(jìn)先調(diào)用的順序執(zhí)行,除非函數(shù)在瀏覽器調(diào)用它之前就到了它的超時時間。
簡化后的大致流程圖如下:
Fiber Node 及 Fiber Tree從流程圖上看到會有 Fiber Node 節(jié)點,這個是在 react 生成的 Virtual Dom 基礎(chǔ)上增加的一層數(shù)據(jù)結(jié)構(gòu),主要是為了將遞歸遍歷轉(zhuǎn)變成循環(huán)遍歷,配合 requestIdleCallback API, 實現(xiàn)任務(wù)拆分、中斷與恢復(fù)。為了實現(xiàn)循環(huán)遍歷,F(xiàn)iber Node 上攜帶了更多的信息。
每一個 Fiber Node 節(jié)點與 Virtual Dom 一一對應(yīng),所有 Fiber Node 連接起來形成 Fiber tree, 是個單鏈表樹結(jié)構(gòu)
兩個階段:reconciliation 和 commit對于異步渲染,現(xiàn)在渲染有兩個階段:reconciliation 和 commit 。前者過程是可以打斷的,后者不能暫停,會一直更新界面直到完成。
reconciliation 處理過程
當(dāng)執(zhí)行 setState() 或首次 render() 時,進(jìn)入工作循環(huán),循環(huán)體中處理的單元為 Fiber Node, 即是拆分任務(wù)的最小單位,從根節(jié)點開始,自頂向下逐節(jié)點構(gòu)造 workInProgress tree(構(gòu)建中的新 Fiber Tree)。
每個工作處理單元做的事情,由 beginWork(), completeUnitOfWork() 兩部分構(gòu)成。
beginWork()主要做的事情是從頂向下生成所有的 Fiber Node,并標(biāo)記 Diff, 不包括兄弟節(jié)點,每個 Fiber Node 的處理過程根據(jù)組件類型略有差異,以 ClassComponent 為例:
1 如果當(dāng)前節(jié)點不需要更新,直接把子節(jié)點clone過來,要更新的話標(biāo)記更新類型
2 更新當(dāng)前節(jié)點狀態(tài)(props, state, context等)
3 調(diào)用shouldComponentUpdate()
4 調(diào)用組件實例方法 render() 獲得新的子節(jié)點,并為子節(jié)點創(chuàng)建 Fiber Node(創(chuàng)建過程會盡量復(fù)用現(xiàn)有 Fiber Node,子節(jié)點增刪也發(fā)生在這里)
5 如果沒有產(chǎn)生 child fiber,進(jìn)入下一階段 completeUnitOfWork
completeUnitOfWork() 當(dāng)沒有子節(jié)點,開始遍歷兄弟節(jié)點作為下一個處理單元,處理完兄弟節(jié)點開始向上回溯,直到再次回去根節(jié)點為止,將收集向上回溯過程中的所有 diff,拿到 diff 后開始進(jìn)入 commit 階段。
構(gòu)建 workInProgress tree 的過程就是 diff 的過程,通過 requestIdleCallback 來調(diào)度執(zhí)行一組任務(wù),每完成一個任務(wù)后回來看看有沒有插隊的(更緊急的),把時間控制權(quán)交還給主線程,直到下一次 requestIdleCallback 回調(diào)再繼續(xù)構(gòu)建workInProgress tree。
兩個階段涉及到的生命周期:Reconciliation 階段 (React算法,用來比較2顆樹,以確定哪些部分需要重新渲染)
componentWillMount
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
Commit 階段 (用于呈現(xiàn)React應(yīng)用的數(shù)據(jù)更改。通常是setState的結(jié)果。最終導(dǎo)致重新渲染。)
componentDidMount
componentDidUpdate
componentWillUnmount
因為 reconciliation 階段是可以被打斷的,所以 reconciliation 階段會執(zhí)行的生命周期函數(shù)就可能會出現(xiàn)調(diào)用多次的情況,從而引起 Bug。所以對于 reconciliation 階段調(diào)用的幾個函數(shù),除了 shouldComponentUpdate 以外,其他都應(yīng)該避免去使用,并且 React16 中也引入了新的 API 來解決這個問題。
于是官方推出了getDerivedStateFromProps,讓你在render設(shè)置新state,你主要返回一個新對象,它就主動幫你setState。由于這是一個靜態(tài)方法,你不能取到 this,當(dāng)然你也不能操作instance,這就阻止了你多次操作setState。這樣一來,getDerivedStateFromProps的邏輯應(yīng)該會很簡單,這樣就不會出錯,不會出錯,就不會打斷DFS過程。
getDerivedStateFromProps取代了原來的componentWillMount與componentWillReceiveProps方法,該函數(shù)會在組件 初始化 和 更新 時被調(diào)用
class ExampleComponent extends React.Component { // Initialize state in constructor, // Or with a property initializer. state = {}; static getDerivedStateFromProps(nextProps, prevState) { if (prevState.someMirroredValue !== nextProps.someValue) { return { derivedData: computeDerivedState(nextProps), someMirroredValue: nextProps.someValue }; } // Return null to indicate no change to state. return null; } }
在進(jìn)入commi階段時,組件多了一個新鉤子叫getSnapshotBeforeUpdate,它與commit階段的鉤子一樣只執(zhí)行一次。
getSnapshotBeforeUpdate 用于替換 componentWillUpdate ,該函數(shù)會在 update 后 DOM 更新前被調(diào)用,用于讀取最新的 DOM 數(shù)據(jù)。
于是整個流程變成這樣:(引用大神@司徒正美的圖)
React16 生命周期函數(shù)用法建議結(jié)合 React Fiber 架構(gòu) 建議如下使用react生命周期
class ExampleComponent extends React.Component { // 用于初始化 state constructor() {} // 用于替換 `componentWillReceiveProps` ,該函數(shù)會在初始化和 `update` 時被調(diào)用 // 因為該函數(shù)是靜態(tài)函數(shù),所以取不到 `this` // 如果需要對比 `prevProps` 需要多帶帶在 `state` 中維護(hù) static getDerivedStateFromProps(nextProps, prevState) {} // 判斷是否需要更新組件,多用于組件性能優(yōu)化 shouldComponentUpdate(nextProps, nextState) {} // 組件掛載后調(diào)用 // 可以在該函數(shù)中進(jìn)行請求或者訂閱 componentDidMount() {} // 用于獲得最新的 DOM 數(shù)據(jù) getSnapshotBeforeUpdate() {} // 組件即將銷毀 // 可以在此處移除訂閱,定時器等等 componentWillUnmount() {} // 組件銷毀后調(diào)用 componentDidUnMount() {} // 組件更新后調(diào)用 componentDidUpdate() {} // 渲染組件函數(shù) render() {} // 以下函數(shù)不建議使用 UNSAFE_componentWillMount() {} UNSAFE_componentWillUpdate(nextProps, nextState) {} UNSAFE_componentWillReceiveProps(nextProps) {} }16 大版本主要更新還解決以下痛點:
組件不能返回數(shù)組,最見的場合是UL元素下只能使用LI,TR元素下只能使用TD或TH,這時這里有一個組件循環(huán)生成LI或TD列表時,我們并不想再放一個DIV,這會破壞HTML的語義。
彈窗問題,之前一直使用不穩(wěn)定的unstable_renderSubtreeIntoContainer。彈窗是依賴原來DOM樹的上下文,因此這個API第一個參數(shù)是組件實例,通過它得到對應(yīng)虛擬DOM,然后一級級往上找,得到上下文。它的其他參數(shù)也很好用,但這個方法一直沒有轉(zhuǎn)正。。。
異常處理,我們想知道哪個組件出錯,雖然有了React DevTool,但是太深的組件樹查找起來還是很吃力。希望有個方法告訴我出錯位置,并且出錯時能讓我有機會進(jìn)行一些修復(fù)工作
HOC的流行帶來兩個問題,畢竟是社區(qū)興起的方案,沒有考慮到ref與context的向下傳遞。
組件的性能優(yōu)化全憑人肉,并且主要集中在SCU,希望框架能干些事情,即使不用SCU,性能也能上去。
新特性:render / 純組件能夠 return 任何數(shù)據(jù)結(jié)構(gòu)
CreatePortal API,更好的處理 Dialog 這種場景組件
新的 context api,嘗試代替一部分 redux 的職責(zé)
異步渲染/時間切片(time slicing),成倍提高性能
componentDidCatch,錯誤邊界,框架層面上提高用戶 debug 的能力
網(wǎng)絡(luò)請求 IO(Suspense),更好的處理異步網(wǎng)絡(luò) IO
參考資料:React Fiber架構(gòu)
React Fiber Architecture
React系列——React Fiber 架構(gòu)介紹資料匯總(翻譯+中文資料)
InterviewMap——React周期分析
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/103129.html
摘要:因為版本將真正廢棄這三生命周期到目前為止,的渲染機制遵循同步渲染首次渲染,更新時更新時卸載時期間每個周期函數(shù)各司其職,輸入輸出都是可預(yù)測,一路下來很順暢。通過進(jìn)一步觀察可以發(fā)現(xiàn),預(yù)廢棄的三個生命周期函數(shù)都發(fā)生在虛擬的構(gòu)建期間,也就是之前。 showImg(https://segmentfault.com/img/bVbweoj?w=559&h=300); 背景 前段時間準(zhǔn)備前端招聘事項...
摘要:在上面我們已經(jīng)知道瀏覽器是一幀一幀執(zhí)行的,在兩個執(zhí)行幀之間,主線程通常會有一小段空閑時間,可以在這個空閑期調(diào)用空閑期回調(diào),執(zhí)行一些任務(wù)。另外由于這些堆棧是可以自己控制的,所以可以加入并發(fā)或者錯誤邊界等功能。 文章首發(fā)于個人博客 前言 2016 年都已經(jīng)透露出來的概念,這都 9102 年了,我才開始寫 Fiber 的文章,表示慚愧呀。不過現(xiàn)在好的是關(guān)于 Fiber 的資料已經(jīng)很豐富了,...
摘要:它的主體特征是增量渲染能夠?qū)秩竟ぷ鞣指畛蓧K,并將其分散到多個幀中。實際上,這樣做可能會造成浪費,導(dǎo)致幀丟失并降低用戶體驗。當(dāng)一個函數(shù)被執(zhí)行時,一個新的堆??蚣鼙惶砑拥蕉褩V?。該堆??虮硎居稍摵瘮?shù)執(zhí)行的工作。 原文 react-fiber-architecture 介紹 React Fibre是React核心算法正在進(jìn)行的重新實現(xiàn)。它是React團(tuán)隊兩年多的研究成果。 React ...
摘要:開始寫代碼構(gòu)造函數(shù)講了那么多的理論,大家一定是暈了,但是沒辦法,架構(gòu)已經(jīng)比之前的簡單要復(fù)雜太多了,因此不可能指望一次性把的內(nèi)容全部理解,需要反復(fù)多看。 前言 Facebook 的研發(fā)能力真是驚人, Fiber 架構(gòu)給 React 帶來了新視野的同時,將調(diào)度一詞介紹給了前端,然而這個架構(gòu)實在不好懂,比起以前的 Vdom 樹,新的 Fiber 樹就麻煩太多。 可以說,React 16 和 ...
閱讀 1920·2021-11-24 11:16
閱讀 3272·2021-09-10 10:51
閱讀 3228·2021-08-03 14:03
閱讀 1275·2019-08-29 17:03
閱讀 3256·2019-08-29 12:36
閱讀 2242·2019-08-26 14:06
閱讀 506·2019-08-23 16:32
閱讀 2708·2019-08-23 13:42