虚拟 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 元素名称。第二个参数是元素的“props”——一个包含要设置在元素上的属性(或特性)的对象。任何其他参数都是元素的子元素,它们可以是字符串(如 '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
试一试!
在此页面的右侧,您将在顶部看到我们先前示例中的代码。其下方是一个框,其中包含运行该代码的结果。您可以编辑代码,并查看您的更改如何影响(或中断!)结果。
要测试您在本教程中学到的内容,请尝试为文本添加更多活力!通过将其包装在 HTML 标签中使单词 World
突出显示:<em>
和 </em>
。
然后,通过添加 style
prop 使所有文本变为 紫色。style
prop 是特殊的,并允许使用一个或多个 CSS 属性的对象值设置在元素上。要将对象作为 prop 值传递,您需要使用 {expression}
,例如 style={{ property: 'value' }}
。