摘要:因為版本將真正廢棄這三生命周期到目前為止,的渲染機制遵循同步渲染首次渲染,更新時更新時卸載時期間每個周期函數各司其職,輸入輸出都是可預測,一路下來很順暢。通過進一步觀察可以發現,預廢棄的三個生命周期函數都發生在虛擬的構建期間,也就是之前。
背景
前段時間準備前端招聘事項,復習前端React相關知識;復習React16新的生命周期:棄用了componentWillMount、componentWillReceivePorps,componentWillUpdate三個生命周期, 新增了getDerivedStateFromProps、getSnapshotBeforeUpdate來代替棄用的三個鉤子函數。
發現React生命周期的文章很少說到 React 官方為什么要棄用這三生命周期的原因, 查閱相關資料了解到根本原因是V16版本重構核心算法架構:React Fiber;查閱資料過程中對React Fiber有了一定了解,本文就相關資料整理出個人對Fiber的理解, 與大家一起簡單認識下 React Fiber;
官方的一句話解釋是“React Fiber是對核心算法的一次重新實現”。Fiber 架構調整很早就官宣了,但官方經過兩年時間才在V16版本正式發布。官方概念解釋太籠統, 其實簡單來說 React Fiber 是一個新的任務調和器(Reconciliation), 本文后續將詳細解釋。
為什么叫 “Fiber”?大家應該都清楚進程(Process)和線程(Thread)的概念,進程是操作系統分配資源的最小單元,線程是操作系統調度的最小單元,在計算機科學中還有一個概念叫做Fiber,英文含義就是“纖維”,意指比Thread更細的線,也就是比線程(Thread)控制得更精密的并發處理機制。
上面說的Fiber和React Fiber不是相同的概念,但是,React團隊把這個功能命名為Fiber,含義也是更加緊密的處理機制,比Thread更細。
為什么官方要花2年多的時間來重構React 核心算法?
首先要從Fiber算法架構前 React 存在的問題說起!說起React算法架構避不開“Reconciliaton”。
React 官方核心算法名稱是 Reconciliation , 中文翻譯是“協調”!React diff 算法的實現 就與之相關。
先簡單回顧下React Diff: React首創了“虛擬DOM”概念, “虛擬DOM”能火并流行起來主要原因在于該概念對前端性能優化的突破性創新;
稍微了解瀏覽器加載頁面原理的前端同學都知道網頁性能問題大都出現在DOM節點頻繁操作上;
而React通過“虛擬DOM” + React Diff算法保證了前端性能;
通過循環遞歸對節點進行依次對比,算法復雜度達到 O(n^3) ,n是樹的節點數,這個有多可怕呢?——如果要展示1000個節點,得執行上億次比較。。即便是CPU快能執行30億條命令,也很難在一秒內計算出差異。
React Diff算法將Virtual DOM樹轉換成actual DOM樹的最少操作的過程 稱為 協調(Reconciliaton)。
React Diff三大策略 :
1.tree diff;
2.component diff;
3.element diff;
PS: 之前H5開發遇到的State 中變量更新但視圖未更新的Bug就是element diff檢測導致。解決方案:1.兩種業務場景下的DOM節點盡量避免雷同; 2.兩種業務場景下的DOM節點樣式避免雷同;
在V16版本之前 協調機制 是 Stack reconciler, V16版本發布Fiber 架構后是 Fiber reconciler。
Stack reconciler Stack reconciler 源碼// React V15: react/src/renderers/shared/stack/reconciler/ReactCompositeComponent.js /** * ------------------ The Life-Cycle of a Composite Component ------------------ * * - constructor: Initialization of state. The instance is now retained. * - componentWillMount * - render * - [children"s constructors] // 子組件constructor() * - [children"s componentWillMount and render] // 子組件willmount render * - [children"s componentDidMount] // 子組件先于父組件完成掛載didmount * - componentDidMount * * Update Phases: * - componentWillReceiveProps (only called if parent updated) * - shouldComponentUpdate * - componentWillUpdate * - render * - [children"s constructors or receive props phases] * - componentDidUpdate * * - componentWillUnmount * - [children"s componentWillUnmount] * - [children destroyed] * - (destroyed): The instance is now blank, released by React and ready for GC. * * -----------------------------------------------------------------------------Stack reconciler 存在的問題
Stack reconciler的工作流程很像函數的調用過程。父組件里調子組件,可以類比為函數的遞歸(這也是為什么被稱為stack reconciler的原因)。
在setState后,react會立即開始reconciliation過程,從父節點(Virtual DOM)開始遍歷,以找出不同。將所有的Virtual DOM遍歷完成后,reconciler才能給出當前需要修改真實DOM的信息,并傳遞給renderer,進行渲染,然后屏幕上才會顯示此次更新內容。
對于特別龐大的DOM樹來說,reconciliation過程會很長(x00ms),在這期間,主線程是被js占用的,因此任何交互、布局、渲染都會停止,給用戶的感覺就是頁面被卡住了。
網友測試使用React V15,當DOM節點數量達到100000時, 加載頁面時間竟然要7秒;詳情
當然以上極端情況一般不會出現,官方為了解決這種特殊情況。在Fiber 架構中使用了Fiber reconciler。
原來的React更新任務是采用遞歸形式,那么現在如果任務想中斷, 在遞歸中是很難處理, 所以React改成了大循環模式,修改了生命周期也是因為任務可中斷。
Fiber reconciler 源碼React的相關代碼都放在packages文件夾里。(PS: 源碼一直在更新,以下路徑有時效性不一定準確)
├── packages --------------------- React實現的相關代碼 │ ├── create-subscription ------ 在組件里訂閱額外數據的工具 │ ├── events ------------------- React事件相關 │ ├── react -------------------- 組件與虛擬DOM模型 │ ├── react-art ---------------- 畫圖相關庫 │ ├── react-dom ---------------- ReactDom │ ├── react-native-renderer ---- ReactNative │ ├── react-reconciler --------- React調制器 │ ├── react-scheduler ---------- 規劃React初始化,更新等等 │ ├── react-test-renderer ------ 實驗性的React渲染器 │ ├── shared ------------------- 公共代碼 │ ├── simple-cache-provider ---- 為React應用提供緩存
這里面我們主要關注 reconciler 這個模塊, packages/react-reconciler/src
├── react-reconciler ------------------------ reconciler相關代碼 │ ├── ReactFiberReconciler.js ------------- 模塊入口 ├─ Model ---------------------------------------- │ ├── ReactFiber.js ----------------------- Fiber相關 │ ├── ReactUpdateQueue.js ----------------- state操作隊列 │ ├── ReactFiberRoot.js ------------------- RootFiber相關 ├─ Flow ----------------------------------------- │ ├── ReactFiberScheduler.js -------------- 1.總體調度系統 │ ├── ReactFiberBeginWork.js -------------- 2.Fiber解析調度 │ ├── ReactFiberCompleteWork.js ----------- 3.創建DOM │ ├── ReactFiberCommitWork.js ------------- 4.DOM布局 ├─ Assist --------------------------------------- │ ├── ReactChildFiber.js ------------------ children轉換成subFiber │ ├── ReactFiberTreeReflection.js --------- 檢索Fiber │ ├── ReactFiberClassComponent.js --------- 組件生命周期 │ ├── stateReactFiberExpirationTime.js ---- 調度器優先級 │ ├── ReactTypeOfMode.js ------------------ Fiber mode type │ ├── ReactFiberHostConfig.js ------------- 調度器調用渲染器入口Fiber reconciler 優化思路
Fiber reconciler 使用了scheduling(調度)這一過程, 每次只做一個很小的任務,做完后能夠“喘口氣兒”,回到主線程看下有沒有什么更高優先級的任務需要處理,如果有則先處理更高優先級的任務,沒有則繼續執行(cooperative scheduling 合作式調度)。
網友測試使用React V16,當DOM節點數量達到100000時, 頁面能正常加載,輸入交互也正常了;詳情
所以Fiber 架構就是用 異步的方式解決舊版本 同步遞歸導致的性能問題。
Fiber 核心算法編程最重要的是思想而不是代碼,本段主要理清Fiber架構內核算法的編碼思路;
Fiber 源碼解析之前一個師弟問我關于Fiber的小問題:
Fiber 框架是否會自動給 Fiber Node打上優先級?
如果給Fiber Node打上的是async, 是否會給給它設置 expirationTime?
帶著以上問題看源碼, 結論:
框架給每個 Fiber Node 打上優先級(nowork, sync, async), 不管是sync 還是 async都會給 該Fiber Node 設置expirationTime, expirationTime 越小優先級越高。
個人閱讀源碼細節就不放了, 因為發現網上有更系統的Fiber 源碼文章,雖然官方源碼已更新至Flow語法, 但算法并沒太大改變:
React Fiber源碼分析 (介紹)
React Fiber源碼分析 第一篇
React Fiber源碼分析 第二篇(同步模式)
React Fiber源碼分析 第三篇(異步狀態)
module.exports = { NoWork: 0, // No work is pending. SynchronousPriority: 1, // For controlled text inputs. Synchronous side-effects. AnimationPriority: 2, // Needs to complete before the next frame. HighPriority: 3, // Interaction that needs to complete pretty soon to feel responsive. LowPriority: 4, // Data fetching, or result from updating stores. OffscreenPriority: 5, // Won"t be visible but do the work in case it becomes visible. };
React Fiber 每個工作單元運行時有6種優先級:
synchronous 與之前的Stack reconciler操作一樣,同步執行
task 在next tick之前執行
animation 下一幀之前執行
high 在不久的將來立即執行
low 稍微延遲(100-200ms)執行也沒關系
offscreen 下一次render時或scroll時才執行
生命周期函數也被分為2個階段了:
// 第1階段 render/reconciliation componentWillMount componentWillReceiveProps shouldComponentUpdate componentWillUpdate // 第2階段 commit componentDidMount componentDidUpdate componentWillUnmount
第1階段的生命周期函數可能會被多次調用,默認以low優先級 執行,被高優先級任務打斷的話,稍后重新執行。
Fiber 架構對React開發影響本段主要探討React V16 后Fiber架構對我們使用React業務編程的影響有哪些?實際編碼需要注意哪些內容。
1.不使用官方宣布棄用的生命周期。為了兼容舊代碼,官方并沒有立即在V16版本廢棄三生命周期, 用新的名字(帶上UNSAFE)還是能使用。 建議使用了V16+版本的React后就不要再使用廢棄的三生命周期。
因為React 17版本將真正廢棄這三生命周期:
到目前為止(React 16.4),React的渲染機制遵循同步渲染:2.注意Fiber 優先級導致的bug;
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
了解Fiber原理后, 業務開發注意高優先級任務頻率,避免出現低優先級任務延遲太久執行或永不執行bug(starvation:低優先級餓死)。
3.業務邏輯實現別太依賴生命周期鉤子函數;在Fiber架構中,task 有可能被打斷,需要重新執行,某些依賴生命周期實現的業務邏輯可能會受到影響。
參考文檔
React 新生命周期;
深入理解進程和線程;
完全理解React Fiber;
React Fiber;
如何閱讀大型項目源碼;
React 源碼解析;
React Fiber 源碼解析;
React Fiber 是什么?
React diff 算法策略和實現
React 新引擎, React Fiber 是什么?
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/106653.html
摘要:在上面我們已經知道瀏覽器是一幀一幀執行的,在兩個執行幀之間,主線程通常會有一小段空閑時間,可以在這個空閑期調用空閑期回調,執行一些任務。另外由于這些堆棧是可以自己控制的,所以可以加入并發或者錯誤邊界等功能。 文章首發于個人博客 前言 2016 年都已經透露出來的概念,這都 9102 年了,我才開始寫 Fiber 的文章,表示慚愧呀。不過現在好的是關于 Fiber 的資料已經很豐富了,...
摘要:什么是每一個都有一個對應的,記錄這個節點的各種狀態,是一鏈表的結構的串聯起來。 1. 什么是fiber 每一個ReactElement都有一個對應的fiber, 記錄這個節點的各種狀態, fiber是一鏈表的結構的串聯起來。showImg(https://segmentfault.com/img/bVbqVZR?w=540&h=708); 2. Fiber的組成 export type...
本文不會過多講解基礎知識,更多說的是在使用useRef如何能擺脫 這個 閉包陷阱 ? react hooks 的閉包陷阱 基本每個開發員都有遇見,這是很令人抓狂的。 (以下react示范demo,均為react 16.8.3 版本) 列一個具體的場景: functionApp(){ const[count,setCount]=useState(1); useEffect(()=...
摘要:它的主體特征是增量渲染能夠將渲染工作分割成塊,并將其分散到多個幀中。實際上,這樣做可能會造成浪費,導致幀丟失并降低用戶體驗。當一個函數被執行時,一個新的堆棧框架被添加到堆棧中。該堆棧框表示由該函數執行的工作。 原文 react-fiber-architecture 介紹 React Fibre是React核心算法正在進行的重新實現。它是React團隊兩年多的研究成果。 React ...
閱讀 3346·2021-11-25 09:43
閱讀 3133·2021-10-11 10:58
閱讀 2734·2021-09-27 13:59
閱讀 3073·2021-09-24 09:55
閱讀 2165·2019-08-30 15:52
閱讀 1826·2019-08-30 14:03
閱讀 2256·2019-08-30 11:11
閱讀 2020·2019-08-28 18:12