什么是虚拟 DOM?(让我们来构建它!)

2025-05-26

什么是虚拟 DOM?(让我们来构建它!)

你可能听说过虚拟 DOM(以及影子 DOM)。你甚至可能使用过它(JSX 本质上就是 VDOM 的语法糖)。如果你想了解更多,那么来对地方了!

在本教程中,我将向您展示什么是虚拟 DOM,并且(可能在本系列的下一篇中)向您展示如何实现我们自己的虚拟 DOM。那就开始吧!

什么是虚拟 DOM?

DOM 操作确实很繁重。如果只进行一次,差异可能看起来很小(给对象赋值属性大约需要 0.4 毫秒),但随着时间的推移,差异会逐渐增大。

// Assigning a property to an object 1000 times
let obj = {};
console.time("obj");
for (let i = 0; i < 1000; i++) {
  obj[i] = i;
}
console.timeEnd("obj");

// Manipulating dom 1000 times
console.time("dom");
for (let i = 0; i < 1000; i++) {
  document.querySelector(".some-element").innerHTML += i;
}
console.timeEnd("dom");
Enter fullscreen mode Exit fullscreen mode

当我运行上面的代码片段时,我发现第一个循环大约花费~3ms,而第二个循环花费~41ms

现在让我们举一个现实生活中的例子。

function generateList(list) {
    let ul = document.createElement('ul');
    document.getElementByClassName('.fruits').appendChild(ul);

    fruits.forEach(function (item) {
        let li = document.createElement('li');
        ul.appendChild(li);
        li.innerHTML += item;
    });

    return ul;
}

document.querySelector("ul.some-selector").innerHTML = generateList(["Banana", "Apple", "Orange"])
Enter fullscreen mode Exit fullscreen mode

到目前为止一切顺利。现在,如果数组发生变化,我们需要重新渲染,我们会这样做:

document.querySelector("ul.some-selector").innerHTML = generateList(["Banana", "Apple", "Mango"])
Enter fullscreen mode Exit fullscreen mode

看看出了什么问题?

即使只有一个元素需要改变,我们也会因为懒惰而改变整个事物

这就是创建虚拟 DOM 的原因。

我知道你已经等待很久了,所以让我们进入正题。

什么虚拟 DOM?

虚拟 DOM 是 DOM 作为对象的表示。因此,假设你有以下 HTML:

<div class="contents">
    <p>Text here</p>
    <p>Some other <b>Bold</b> content</p>
</div>
Enter fullscreen mode Exit fullscreen mode

它可以写成以下 VDOM 对象:

let vdom = {
    tag: "div",
    props: { class: 'contents' },
    children: [
        {
            tag: "p",
            children: "Text here"
        },
        {
            tag: "p",
            children: ["Some other ", { tag: "b", children: "Bold" }, " content"]
        }

    ]
}
Enter fullscreen mode Exit fullscreen mode

请注意,现实生活中可能存在更多属性,这是一个简化版本。

我确信这是不言自明的,特别是如果你用过 React。如果没有的话:

  • VDOM 基本上是一个对象,
    • 属性称为tag(有时也称为type),基本上是标签的名称
    • props包含所有 props 的属性
    • children名为的属性
    • 如果内容只是文本,则为字符串
    • 如果内容包含元素,则为 VDOM 数组

我们像这样使用 VDOM:

  • 我们对 VDOM 进行更改,而不是 DOM
  • 函数检查 DOM 和 VDOM 之间的所有差异,并仅更改真正改变的内容
  • 刚刚用于更改内容的 VDOM 被标记为最新更改,以便我们下次可以比较 VDOM,从而节省更多

有什么好处?

我确信你现在已经明白了,但这里有一个实际的例子。让我们以之前的generateList函数为例,并对其进行改进:

function generateList(list) {
    // VDOM generating stuff which I will explain later...
}

patch(oldUL, generateList(["Banana", "Apple", "Orange"]));
Enter fullscreen mode Exit fullscreen mode

不要介意补丁功能,它基本上将更改附加到 DOM。

现在,当我们将来再次更改 DOM 时:

patch(oldUL, generateList(["Banana", "Apple", "Mango"]));
Enter fullscreen mode Exit fullscreen mode

patch 函数发现只有第三个li发生了变化,并且只有第三个li发生了变化,而不是改变所有三个元素

这就是 VDOM 的全部内容,下一部分我将向您展示如何实现 VDOM

文章来源:https://dev.to/siddharthshyniben/what-is-the-virtual-dom-let-s-build-it-5070
PREV
使用动态导入和交叉观察器延迟加载反应组件
NEXT
Deno:Node.js 的下一步