React 元件概念與效能調整 從 Element、Reconciliation 概念出發調整效能

Lin Yen-Cheng on 2020-08-05 5 min. read

當 React 元件遇到效能瓶頸時,在重構或解析元件需從 Element、Reconciliation 概念開始了解,React 主要幫我們將狀態轉換成畫面,另外

  • 讓開發者用 JavaScript 處理所有事情
  • 提供 JSX 支援
  • 將邏輯和渲染分開,所以寫一套邏輯可渲染在不同平台
  • Reconciliation 跟 render 是不同階段的動作,最佳化算法 (fiber) 也可以套用在不同平台

Element X JSX

元件中的 JSX 會透過 createElement 這個 function 被轉換成 React Element。

React Element 是一個物件,包含 type 及 properties,type 用來區分是 component instance 或是 DOM node,Element 在 React 中又分成兩種

  1. Element (type 為 DOM node)
{
  type: 'button',
  props: {
    className: 'button button-blue',
    children: {
      type: 'b',
      props: {
        children: 'OK!'
      }
    }
  }
}

<button class='button button-blue'>
  <b>
    OK!
  </b>
</button>
  1. Component Element (type 為 instance)
const DeleteAccount = () => (&#123;
  type: SubmitForm,
  props: &#123;
    children: [&#123;
      type: 'p',
      props: &#123;
        children: 'Are you sure?'
      &#125;
    &#125;, &#123;
      type: Button,
      props: &#123;
        color: 'blue',
        children: 'Cancel'
      &#125;
   &#125;]
&#125;);

const DeleteAccount = () => (
  <SubmitForm>
    <p>Are you sure?</p>
    <Button color='blue'>Cancel</Button>
  </SubmitForm>
);

Virtual Dom

當我們知道底層的定義之後,要先記住 React 在做的事情:

React.js 就是給狀態然後出畫面

瀏覽器中的畫面是 DOM tree 構成,因為 DOM tree 的操作是成本高的動作,可以參考 state of the art algorithms O(n3),React 效能好就歸功於寫的程式碼並不會操作 DOM,而是會有以下特性:

  • 改變的狀態其實是操作我們常聽到的 Virtual Dom
  • 狀態對 UI 影響是非同步,如果狀態在一個循環內 A -> B -> C -> A 這樣最後 DOM 就不會變化
  • 相同 type,如果 attributes 改變則會偵測並變化 attribute
  • Reconciliation (fiber) 將複雜度減少到 O(n)

所以 React 也會這樣的資料結構中,處理狀態對於元件的改變,這個結構就是我們常聽的 Virtual Dom,有篇開箱文寫的很棒:

https://pomb.us/build-your-own-react/

在這篇文章中可以看成是一棵 left-chlid right-sibling tree:

  • 每一個 Node 有指向 Parent 的參考
  • 過程先看有沒有 Child,再看有沒有 Sibling,都沒有才回到 Parent
  • 整棵樹走完才 render 結果到實際的 DOM 上

Reconciliation

Reconciliation 就是一個演算法,找出哪些樹節點哪些需要變化,當我們呼叫 render() 的時候,React 會做一個 Top-Down 的 Reconciliation。

過程中會不停地去問你的 type 是什麼? 如果我們定義了一個 Component Elements X 且 type 是 Y,那 React 就會去問什麼是 Y,直到問到最基礎的組成為止。

Reconciliation 實作上會符合以下假設:

  • 不同 type,在 react 會產生不同的樹,不會去偵測而是直接取代
  • 偵測一個 list 的改變會透過 keys 來增加效能

<ul>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

<ul>
  <li key="2014">Connecticut</li>
  <li key="2015">Duke</li>
  <li key="2016">Villanova</li>
</ul>

校能優化注意事項

  • 不要去修改 dom 的 attribute,讓 React 處理
  • key 盡量用後端給出來不易重複的值
  • setState 是 batch 的非同步動作,所以要注意狀態是 immutable
  • 下個狀態與前一個狀態有關的時候,setState((prveState) => {...prevState, newState});

喜歡這篇文章,請幫忙拍拍手喔 🤣

share