iT邦幫忙

2021 iThome 鐵人賽

DAY 7
0
Modern Web

不只懂 Vue 語法:Vue.js 觀念篇系列 第 7

不只懂 Vue 語法:什麼是 Virtual DOM?Vue 如何利用 Virtual DOM?

  • 分享至 

  • xImage
  •  

問題回答

當我們更新資料和渲染畫面時會頻繁地新增和刪除 DOM 元素,造成效能問題。因此,不論是 Vue 或 React 都有使用 Virtual DOM 來避免直接操控 DOM。當 Vue 偵測到資料有更新,就會再次渲染更新的部分。但為了效能的考慮,它不會把整個 DOM 換掉,而是先建立一個 Virtual DOM,與原本的 DOM 作出比較,並透過算法找出有差異的部分,再針對它們來更新舊有的 DOM。

以下會再作詳細解說。

什麼是 DOM、Virtual DOM?

DOM 是以樹狀模型來看 HTML 文件

在進入主題前,先稍為重溫什麼是 DOM。DOM 是以樹狀結構來顯示一個 HTML 文件的模型,這個樹模型由一個個 DOM 節點(元素)組成。Web APIs 有提供 document.createElementdocument.body.appendChild() 等語法來操控 DOM。

圖片來源:https://www.runoob.com/js/js-htmldom.html

以上圖為例,左邊有一個 h1 標籤,所以在該 HTML 文件中,應該會找到這個 DOM 節點:

<h1> 標題 </h1>

Virtual DOM 的意思

Virtual DOM 是透過 JavaScript 物件模擬 一個 DOM 節點(Node)。之後再經由負責渲染的函式,變成真正的 DOM 節點,最後被掛載到網頁裏,完成更新 DOM。

Vue 如何使用 Virtual DOM

Vue 的 Virtual DOM 是參考 Snabbdom 這個 Virtual DOM 套件來實現。在 Vue 我們把 Virtual DOM 稱為 VNode。

簡單總括流程如下:

  1. 用 JavaScript 物件記錄現有的 DOM 結構
  2. 當資料狀態改變時,建立 Virtual DOM 以及一個新的 DOM 結構
  3. 利用算法分析這兩個 DOM 的差異,記錄實際上要更新的 DOM
  4. 針對要更新的 DOM,執行渲染的函式,把 Virtual DOM 渲染到畫面上,完成更新 DOM

關於第二點,之前的文章有提到 Vue 使用了 MVVM 模式:

View(畫面) <---> ViewModel <---> Model (資料)

ViewModel 會監聽 View 各種 DOM 的事件,並與相關的 Model 作綁定。當畫面觸發事件,就會觸發修改資料,之後再跑渲染資料到畫面的流程。

Vue 所使用 diff 算法的基本概念

Vue 使用 diff 算法來避免在更新 DOM 時把整個 DOM 全都更新,而是只針對有更動的 DOM 來作更新,提升效能。

在此簡單總括 diff 算法。當資料有更動時,Vue 就會產生兩個 DOM。一是 Virtual DOM,二是原本舊有的 DOM。此算法就會以同層級比較的方式,比較兩者的 DOM 節點的差異,並針對作出更新。有關詳細針對 Vue 原始碼和概念解說,可參考這篇文章

實作一個 Virtual DOM

算法的部分就不在此深究,但可以試試用 JavaScript 練習實作一個 Virtual DOM 來理解概念。例如我想建立一個 DOM:

<div id="app">
    <a href="https://google.com">
        這是一段文字
    </a>
</div>

用 JavaScript 物件來表示:

const exampleNode = {
  tagName: "div",
  attrs: {
    id: "app",
  },
  children: [
    {
      tagName: "a",
      attrs: {
        href: "https://google.com",
      },
      children: ["這是一段文字"],
    },
  ],
};

按這樣的結構,我們先建立一個負責產生 Virtual DOM 的 Class

class Element {
  constructor(tagName, { attrs = {}, children = [] }) {
    this.tagName = tagName;
    this.attrs = attrs;
    this.children = children;
  }
}

我們會用 Element 這個 Class 來建構出 Virtual DOM 的物件。接下來把之前提到的 DOM 結構,透過使用 Element Class 來建構:

const VNode = new Element("div", {
  attrs: {
    id: "app",
  },
  children: [
    new Element("a", {
      attrs: {
        href: "https://google.com",
      },
      children: ["這是一段文字"],
    }),
  ],
});

console.log(VNode) 查看目前 VNode 的值:

目前完成以 JavaScript 物件方式來顯示我們想要達成的 DOM 結構。接下來就是把它轉換為真正的 DOM。在 Element 加入 renderElement 函式,並使用 document.createElementElement.setAttribute()Element.appendChild 等 Web APIs 來建立真正的 DOM:

class Element {
  constructor(tagName, { attrs = {}, children = [] }) {
    this.tagName = tagName;
    this.attrs = attrs;
    this.children = children;
  }

  renderElement() {
    const element = document.createElement(this.tagName);

    for (const [attrName, attrValue] of Object.entries(this.attrs)) {
      element.setAttribute(attrName, attrValue);
    }

    this.children.forEach((child) => {
      const childElement =
        // 如果此 child 不是以 Element 建構出來,就代表它是 textNode
        child instanceof Element
          ? child.renderElement()
          : document.createTextNode(child); 

      // 把子 Node 塞進 父 Node 裏
      element.appendChild(childElement);
    });

    return element;
  }
}

呼叫 renderElement 的方法,就能產出一個真正的 DOM:

const result = VNode.renderElement()

console.log(result) 看看結果:

最後把它掛載到網頁上:

document.getElementById("app").appendChild(result);

完整程式碼

https://codesandbox.io/s/yong-javascript-wu-jian-shi-zuo-virtual-dom-0wcjj?file=/src/index.js

總結

  • Vue、React 都有使用 Virtual DOM 來提升更新 DOM 的效能
  • Virtual DOM 是指利用 JavaScript 物件來模擬一個 DOM 節點的結構
  • 當資料更新時,會比較現有已更新的 Virtual DOM 和舊有的 DOM,並針對差異之處來更新 DOM

參考資料

Vue.js 技术揭秘 - Virtual DOM
深度剖析:如何实现一个 Virtual DOM 算法
從頭打造一個簡單的 Virtual DOM


上一篇
不只懂 Vue 語法:Vue 3 如何使用 Proxy 實現響應式(Reactivity)?
下一篇
不只懂 Vue 語法:請說明 style 裏的 scoped、deep selector 的作用?
系列文
不只懂 Vue 語法:Vue.js 觀念篇31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Global Rachel
iT邦新手 3 級 ‧ 2022-02-15 18:01:28

這系列觀念很深,受益良多!感謝文章分享~(一定花了超多時間!!

我要留言

立即登入留言