iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 21
1
Modern Web

React.js 從 【0】 到【1】推坑計畫 系列 第 21

【Day 21】React 渲染機制

我們都知道使用 React 讓我們的應用可以擁有非常好的效能,但背後裡 React 到底幫我們做了哪些努力呢?
今天就來初探一下 React 的渲染機制!

當我們開啟頁面時, React 透過 render 函式建構出一棵 DOM 樹出來,之後每次 state 或 props 變動時 ,React 會建構出另一個虛擬的 DOM 出來,來與真實的 DOM 做比較,這就是之前講過的 Virtual DOM的概念。

Diff 演算法

在執行 Diff 演算法時, React 有個假設前提:

  • 兩個相同元件產生類似的 DOM 結構,不同元件產生不同的 DOM 結構。

基於這點假設,Diff 演算法只會針對同層節點進行比較
Diff 也極大的優化了這個比較的過程,將演算法複雜度從O(n^3)降低到O(n)。

https://ithelp.ithome.com.tw/upload/images/20190925/20113277C5TvF2niIl.png

如果父結點不同,React 不會再去比較子結點。這提高了比對的效率。

阻止不必要的重新渲染

還記得好幾天前講的渲染過程嗎?

constructor -> render -> componentDidMount -> render(re-render by updating) -> componentDidUpdate -> componentWillUnmount

我們知道當 props 或 state 改動時會觸發元件的重新渲染(re-render)
而當父元件重新渲染時也會迫使它的子元件 re-render,然而我們清楚有些元件其實內容是寫死的,或是它的 props 根本就沒有改變,卻還是得依照慣例重新渲染,難道我們只能眼睜睜看著效能浪費嗎?

shouldComponentUpdate

在 Day 9 介紹 lifecycle 時附上一張 React 組件的生命週期圖,還記得上面有個 shouldComponentUpdate 函式嗎?他就是拿來避免不必要的重新渲染的。
如果你在這個 function 中回傳 false,就不會重新呼叫 render function。

shouldComponentUpdate () {
    return false;
  }

也就是說當 componentDidMount 後再經過 render 後,元件就不會再重新渲染了。
但這樣寫死風險還是太大了,萬一真的需要重新渲染就被卡死在這了,因此一般來說我們會這樣寫:

shouldComponentUpdate(nextProps, nextState){
    return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);
  }

當 props 或 state 有變動時才回傳 true。
至於使用 shallowCompare 則是效能考量,有興趣的讀者可以自行研究一下囉!

其實 class based 的 PureCompponent 就是在背後幫我們加入了 shouldComponentUpdate 的判斷, 看到這你可能又會突破盲點了:“可是我們前幾天都用 functional 的 元件欸,不能用生命週期怎麼實現這件事?”
別急,明天就來介紹在 functional 元件中如何達成這件事。

最後提醒一下:當你確定你的 component props 或 state 會時常改變時,使用 shouldComponentUpdate 機制反而會多了不必要的 shollowCompare 使效能不增反減喔,所以使用時機必須非常仔細地去拿捏!


上一篇
【Day 20】可能不需要redux (2) - contextAPI
下一篇
【Day 22】React.memo
系列文
React.js 從 【0】 到【1】推坑計畫 30

尚未有邦友留言

立即登入留言