虛擬 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 樹
從最簡單的方法開始會很有幫助,也就是直接呼叫 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' }}
。