說明
支持我們

虛擬 DOM

您可能聽過人們提到「虛擬 DOM」,並感到疑惑:什麼讓它「虛擬」?「虛擬」DOM 與我們在瀏覽器程式設計時使用的真實 DOM 有何不同?

虛擬 DOM 是使用物件對樹狀結構的簡單描述

let vdom = {
  type: 'p',         // a <p> element
  props: {
    class: 'big',    // with class="big"
    children: [
      'Hello World!' // and the text "Hello World!"
    ]
  }
}

Preact 等函式庫提供建構這些描述的方式,然後可以將這些描述與瀏覽器的 DOM 樹進行比較。當樹的每個部分進行比較時,瀏覽器的 DOM 樹會更新,以符合虛擬 DOM 樹所描述的結構。

這是一個有用的工具,因為它讓我們可以宣告式地而非命令式地組合使用者介面。我們不需要描述如何根據鍵盤或滑鼠輸入等事項更新 DOM,我們只需要描述在收到該輸入後 DOM 應該如何顯示。這表示我們可以重複提供樹狀結構的 Preact 描述,而它會更新瀏覽器的 DOM 樹以符合每個新描述,而不管其目前的結構為何。

在本章中,我們將學習如何建立虛擬 DOM 樹,以及如何指示 Preact 更新 DOM 以符合這些樹。

建立虛擬 DOM 樹

有幾種方法可以建立虛擬 DOM 樹

  • createElement():Preact 提供的函式
  • JSX:可以編譯成 JavaScript 的類 HTML 語法
  • HTM:您可以直接在 JavaScript 中撰寫的類 HTML 語法

從最簡單的方法開始會很有幫助,也就是直接呼叫 Preact 的 createElement() 函式

import { createElement, render } from 'preact';

let vdom = createElement(
  'p',              // a <p> element
  { class: 'big' }, // with class="big"
  'Hello World!'    // and the text "Hello World!"
);

render(vdom, document.body);

上述程式碼建立段落元素的虛擬 DOM「描述」。createElement 的第一個引數是 HTML 元素名稱。第二個引數是元素的「屬性」- 包含要設定在元素上的屬性(或特性)的物件。任何其他引數都是元素的子項目,它們可以是字串(例如 'Hello World!')或來自其他 createElement() 呼叫的虛擬 DOM 元素。

最後一行指示 Preact 建立符合我們的虛擬 DOM「描述」的真實 DOM 樹,並將該 DOM 樹插入網頁的 <body> 中。

現在有更多 JSX!

我們可以使用 JSX 重寫前一個範例,而不用變更它的功能。JSX 讓我們使用類似 HTML 的語法來描述我們的段落元素,這有助於在我們描述更複雜的樹狀結構時保持可讀性。JSX 的缺點是我們的程式碼不再使用 JavaScript 編寫,而且必須由類似 Babel 的工具來編譯。編譯器會將以下的 JSX 範例轉換成我們在前一個範例中看到的確切 createElement() 程式碼。

import { createElement, render } from 'preact';

let vdom = <p class="big">Hello World!</p>;

render(vdom, document.body);

它現在看起來更像 HTML 了!

關於 JSX,還有一件事需要記住:JSX 元素內部的程式碼(在尖括號內)是特殊語法,而不是 JavaScript。若要使用數字或變數等 JavaScript 語法,你首先需要使用 {expression} 從 JSX 跳出來,類似於範本中的欄位。以下範例顯示兩個表達式:一個是將 class 設定為隨機字串,另一個是計算數字。

let maybeBig = Math.random() > .5 ? 'big' : 'small';

let vdom = <p class={maybeBig}>Hello {40 + 2}!</p>;
                 // ^---JS---^       ^--JS--^

如果我們要 render(vdom, document.body),就會顯示文字「Hello 42!」。

再次使用 HTM

HTM 是 JSX 的替代方案,它使用標準 JavaScript 標籤範本,消除了編譯器的需求。如果你沒有遇過標籤範本,它們是一種特殊型式的字串文字,可以包含 ${expression} 欄位

let str = `Quantity: ${40 + 2} units`;  // "Quantity: 42 units"

HTM 使用 ${expression},而不是 JSX 中的 {expression} 語法,這可以更清楚地顯示程式碼的哪些部分是 HTM/JSX 元素,哪些部分是純 JavaScript

import { html } from 'htm/preact';

let maybeBig = Math.random() > .5 ? 'big' : 'small';

let vdom = html`<p class=${maybeBig}>Hello ${40 + 2}!</p>`;
                        // ^--JS--^          ^-JS-^

所有這些範例都會產生相同的結果:一個虛擬 DOM 樹,可以提供給 Preact 來建立或更新現有的 DOM 樹。


繞道:元件

我們將在稍後的教學中深入探討元件,但目前重要的是要知道像 <p> 的 HTML 元素只是虛擬 DOM 元素的兩種類型之一。另一種類型是元件,它是一個虛擬 DOM 元素,其中類型是函式,而不是像 p 的字串。

元件是虛擬 DOM 應用程式的組成要素。目前,我們將透過將 JSX 移至函式中來建立一個非常簡單的元件

import { createElement } from 'preact';

export default function App() {
    return (
        <p class="big">Hello World!</p>
    )
}

render(<App />, document.getElementById("app"));

將元件傳遞給 render() 時,讓 Preact 進行實例化,而不是直接呼叫元件非常重要,否則會以意外的方式中斷

const App = () => <div>foo</div>;

// DON'T: Invoking components directly breaks hooks and update ordering:
render(App(), rootElement); // ERROR
render(App, rootElement); // ERROR

// DO: Passing components using createElement() or JSX allows Preact to render correctly:
render(createElement(App), rootElement); // success
render(<App />, rootElement); // success

試試看!

在此頁面的右側,您將在頂端看到我們先前範例的程式碼。下方有一個方塊,顯示執行該程式碼的結果。您可以編輯程式碼,並查看您的變更如何影響(或中斷!)結果。

若要測試您在本章節中所學到的內容,請嘗試讓文字更生動!透過將 World 字詞包在 HTML 標籤 <em></em> 中,讓它脫穎而出。

然後,透過新增 style prop,讓所有文字都變成 紫色style prop 很特別,它允許物件值包含一個或多個 CSS 屬性,以設定在元素上。若要將物件傳遞為 prop 值,您需要使用 {expression},例如 style={{ property: 'value' }}

載入中...