react 中的異步渲染

header-11ea97c36869e5a3314f5c140d2d1b6d-7930b.jpeg

React Fiber

  • Diff 算法

熟悉 react 的朋友都知道,在 react 中有個核心的算法,叫 diff 算法。web 界面由 dom 樹組成,不一樣的 dom 樹會渲染出不一樣的界面。react 使用 virtual dom 來表示 dom 樹,而 diff 算法就是用於比較 virtual dom 樹的區別,並更新界面須要更新的部分。diff 算法和 virtual dom 的完美結合的過程被稱爲 reconciler,這但是 react 攻城拔寨的絕對利器。有了 reconciler,開發者能夠脫身操做真實的 dom 樹,只須要向 react 描述界面的狀態,而 react 會幫助你高效的完成真正 dom 操做。
reconciler-d8d7812617894d5c10b9da6816e4be89-8bb97.jpegreact

在 react16 以前的 reconciler 叫 stack reconciler,fiber 是 react 新的 reconciler,此次更新到 fiber 架構是一次重量級的核心架構的替換,react 爲了完成此次替換已經準備了兩三年的時間了。程序員

那麼 fiber 究竟有什麼好的呢?web

Fiber 爲什麼出現

不知道你們有沒有遇到過這樣的狀況,點擊一個頁面的按鈕時感受到頁面沒有任何的反應,讓你懷疑電腦是否是死機了,而後你快速切出瀏覽器,發現電腦並無死機,因而再切回瀏覽器,這時候才發現頁面終於更新了。爲何會出現這種狀況?在多數狀況下,多是由於瀏覽器忙着執行相關的 js 代碼,致使瀏覽器主線程沒有及時響應用戶的操做或者沒有及時更新界面。下面這張圖就表示了這種現象,你的公司只有一個程序員 (main thread),當這個程序員在執行你的任務 (your code) 時,處於沉浸式編程的狀態,沒法響應外部的其餘事件,什麼下班吃飯,都是不存在的。這就像瀏覽器忙着執行 js 代碼的時候,不會去執行頁面更新等操做。
mainthread-eb46d69eaaeb4f2644082b4cf6525fb8-db9b4.jpeg算法

本着顧客是上帝的原則,做爲一名優秀的開發者,怎麼可以容許出現這種狀況下降用戶的體驗呢。所以 react 團隊引入了異步渲染這個概念,而採用 fiber 架構能夠實現這種異步渲染的方式。編程

原先的 stack reconciler 像是一個遞歸執行的函數,從父組件調用子組件的 reconciler 過程就是一個遞歸執行的過程,這也是爲何被稱爲 stack reconciler 的緣由。當咱們調用 setState 的時候,react 從根節點開始遍歷,找出全部的不一樣,而對於特別龐大的 dom 樹來講,這個遞歸遍歷的過程會消耗特別長的時間。在這個期間,任何交互和渲染都會被阻塞,這樣就給用戶一種「死機」的感受。api

stackreconciler-e90e3cd4afad3fe3a0684ce14865d195-87f44.jpeg

fiber 的出現解決了這個問題,它把 reconciler 的過程拆分紅了一個個的小任務,並在完成了小任務以後暫停執行 js 代碼,而後檢查是否有須要更新的內容和須要響應的事件,作出相應的處理後再繼續執行 js 代碼。這樣就給了用戶一種應用一直在運行的感受,提升了用戶的體驗。
fiberreconciler-cf0ee9cf8c460da97f5a377b5d6b6267-8598e.jpeg瀏覽器

Fiber 如何作到異步渲染

在作顯示方面的工做時,常常會聽到一個目標叫 60 幀,這表示的是畫面的更新頻率,也就是畫面每秒鐘更新 60 次。這是由於在 60 幀的更新頻率下,頁面在人眼中顯得流暢,無明顯卡頓。每秒鐘更新 60 次也就是每 16ms 須要更新一次頁面,若是更新頁面消耗的時間不到 16ms,那麼在下一次更新時機來到以前會剩下一點時間執行其餘的任務,只要保證及時在 16ms 的間隔下更新界面就徹底不會影響到頁面的流暢程度。fiber 的核心正是利用了 60 幀原則,實現了一個基於優先級和 requestIdleCallback 的循環任務調度算法。服務器

requestIdleCallback-18eba080f1aa2a3ddaa538b4827158e1-50b73.jpeg

requestIdleCallback 是瀏覽器提供的一個 api,可讓瀏覽器在空閒的時候執行回調,在回調參數中能夠獲取到當前幀剩餘的時間,fiber 利用了這個參數,判斷當前剩下的時間是否足夠繼續執行任務,若是足夠則繼續執行,不然暫停任務,並調用 requestIdleCallback 通知瀏覽器空閒的時候繼續執行當前的任務。數據結構

function fiber(剩餘時間) {
 if (剩餘時間 > 任務所需時間) {
 作任務;
 } else {
 requestIdleCallback(fiber);
 }
}

fiber 還會爲不一樣的任務設置不一樣的優先級,高優先級任務是須要立刻展現到頁面上的,好比你正在輸入框中輸入文字,你確定但願你的手指在鍵盤上敲下每個按鍵時,輸入框能立馬作出反饋,這樣你才能知道你的輸入是否正確,是否有效。低優先級的任務則是像從服務器傳來了一些數據,這個時候須要更新頁面,好比這篇文章喜歡的人數+1 或是評論+1,這並非那麼緊急的更新,延遲 100-200ms 並不會有多大差異,徹底能夠在後面進行處理。fiber 會根據任務優先級來動態調整任務調度,優先完成高優先級的任務。架構

{ 
 Synchronous: 1, // 同步任務,優先級最高
 Task: 2, // 當前調度正執行的任務
 Animation 3, // 動畫
 High: 4, // 高優先級
 Low: 5, // 低優先級
 Offscreen: 6, // 當前屏幕外的更新,優先級最低
}

在 fiber 架構中,有一種數據結構,它的名字就叫作 fiber,這也是爲何新的 reconciler 叫作 fiber 的緣由。fiber 其實就是一個 js 對象,這個對象的屬性中比較重要的有 stateNode、tag、return、child、sibling 和 alternate。

Fiber = {
 tag // 標記任務的進度
 return // 父節點
 child // 子節點
 sibling // 兄弟節點
 alternate // 變化記錄
 .....
};

咱們能夠看出 fiber 基於鏈表結構,擁有一個個指針,指向它的父節點子節點和兄弟節點,在 diff 的過程當中,依照節點鏈接的關係進行遍歷。

fiber 可能存在的問題
在 fiber 中,更新是分階段的,具體分爲兩個階段,首先是 reconciliation 的階段,這個階段在計算先後 dom 樹的差別,而後是 commit 的階段,這個階段將把更新渲染到頁面上。第一個階段是能夠打斷的,由於這個階段耗時可能會很長,所以須要暫停下來去執行其餘更高優先級的任務,第二個階段則不會被打斷,會一口氣把更新渲染到頁面上。

hookfunction-b8fa3821391fe30eaf4194064c228e81-97d7d.jpeg
因爲 reconciliation 的階段會被打斷,可能會致使 commit 前的這些生命週期函數屢次執行。react 官方目前已經把 componentWillMount、componentWillReceiveProps 和 componetWillUpdate 標記爲 unsafe,並使用新的生命週期函數 getDerivedStateFromProps 和 getSnapshotBeforeUpdate 進行替換。

還有一個問題是飢餓問題,意思是若是高優先級的任務一直插入,致使低優先級的任務沒法獲得機會執行,這被稱爲飢餓問題。對於這個問題官方提出的解決方案是儘可能複用已經完成的操做來緩解。相信官方也正在努力提出更好的方法去解決這個問題。

原文連接:https://knownsec-fed.com/2018...

相關文章
相關標籤/搜索