React原理 – Virtual DOM

建議先備知識:DOM 渲染原理

現代網頁複雜性越來越高,一個網頁的 DOM Tree 動轍幾百個節點,如果我們每次操作 DOM,都要重新渲染頁面,那成本似乎有點太高了吧!於是就有人想出了 Virtual DOM,在 JS 和 DOM 之間做一個緩衝,渲染之前先比較新舊 DOM 的差異,再只針對有變化的部分做 DOM 操作,如此就能解決重新渲染的成本問題。

所以 Virtual DOM(虛擬 DOM)是什麼?

Virtual DOM 本質上就是一個 JS 物件,它是一種用 JS 物件描述 DOM 的方式

<div id="app">
  <p class="text">hello world!!!</p>
</div>

上面的 HTML 會被 React 轉換為 Virtual DOM(如下),就是一個 JS 物件

{
  tag: 'div',
  props: {
    id: 'app'
  },
  chidren: [
    {
      tag: 'p',
      props: {
        className: 'text'
      },
      chidren: [
        'hello world!!!'
      ]
    }
  ]
}

Virtual DOM 優點

  • 跨平台,Virtual DOM 並非一定要渲染成瀏覽器 DOM,也可以渲染成 Native App 的元素,或是做 SSR(服務器端渲染)
  • 減少性能浪費,總是以最小成本進行更新。當狀態更動,透過 Diff 算法比較、找出差異,再更新到 DOM Tree 上

要注意的是,Virtual DOM 的性能未必比直接操作 DOM 快,它的優點是在於,不管狀態怎麼變化,都能以最小成本來更新 DOM

Virtual DOM 運作流程

  1. 產生 Virtual DOM
  2. 根據 Virtual DOM 產生 Real DOM
  3. 當某事件被觸發,狀態(state)發生變化
  4. 產生新的 Virtual DOM
  5. 比較新舊 Virtual DOM 差異(透過 Diff 算法)
  6. 把差異 Patch 到 Real DOM 上


圖片來源:https://medium.com/geekabyte/lets-better-know-the-famous-vdom-a21faf9e9157

React Diff 算法簡介

Diff 是 diffrence 的簡寫,目的在找出新舊 Virtual DOM 的差異,

一般的 tree diff 算法時間複雜度很高,而 React 的 diff 則制定了兩個假設來降低其複雜度

  1. 同層比對,當發現有一層有差異,表示往下的樹就不同
  2. 開發者可透過 key prop 來決定其子樹是否需要重新 render

算法的細節比較複雜,如果想更深入了解可自行研究:
深度剖析:如何实现一个 Virtual DOM 算法
React 源码深度解读(十):Diff 算法详解

為什麼不建議用 index 當 key?

大家應該知道 React 不建議在循環列表時使用 index 當 key,為什麼?

這是因為我們的新舊 Virtual DOM 上的 key 必須要一致,才會提升 Diff 在比對時的效率,而使用 index 的話,就無法確保 key 一致了

舉個例子,假設有一個列表用 index 當 key

<li key=0>a</li>
<li key=1>b</li>
<li key=2>c</li>

然後狀態更動 刪除 a,就會變成

<li key=0>b</li>
<li key=1>c</li>

可以看到,b 的 key 不相同了,這樣就無法建立關聯性,對性能上完全沒有幫助,
因此好的做法還是給其穩定、不變的 key 值

Reference:
從頭打造一個簡單的 Virtual DOM
虚拟 DOM 到底是什么?
https://zh-hant.reactjs.org/docs/reconciliation.html

Similar Posts