这篇文章上次修改于 2026 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

什么是Virtual DOM?

所谓virtual,指的是对真实DOM的一种模拟。相对于直接操作真实的DOM结构,我们构建一棵虚拟的树,将各种数据和操作直接应用在这棵虚拟的树上,然后再将对虚拟的树的修改应用到真实的DOM结构上。
这样有以下好处:

  1. 可能会减少DOM操作次数,带来性能上的提升,即使我们频繁操作虚拟DOM,我们只需要一定时刻一次性同步修改到真实DOM上即可
  2. 真实的HTML节点有两百多个属性和方法,使用虚拟节点,能在数据结构上节省内存
  3. 虚拟DOM不依赖浏览器环境,能在node环境实现,可以使用虚拟DOM生成html字符串,实现SSR

使用新的数据结构表示HTML节点

尝试在控制台打印一个HTML节点的所有属性和方法,我们会发现HTML节点是一个非常巨大的数据结构。

img

HTMLElement由两百多个属性和方法

而90%以上的属性和方法我们并不关心。

对Element节点的表示

所以我们可以尝试用下面的结构表示Element节点。

export default class VNode {
    constructor(tagName, attrs, children) {
        this.tagName = tagName 
        this.attributes = attrs
        this.children = children
    }
}

那么对于:

<div class='container'></div>

我们就能用VNode('div', {class: 'container'}, [])来表示了。

然后我们为VNode添加render方法,实现向真实节点的转换。

class VNode {
    constructor(tagName, attrs, children) {
        this.tagName = tagName
        this.attributes = attrs
        this.children = children
    }

    render() {
        let element = document.createElement(this.tagName)
        Object.keys(this.attributes).forEach(key => {
            element.setAttribute(key, this.attributes[key])
        })

        this.children.forEach(child => {
            element.appendChild(child.render())
        })

        return element
    }
}

对于Text节点的表示

对于text节点,暂时只关心文本内容

class TextNode {
    constructor(content) {
        this.content = content
    }

    render() {
        return document.createTextNode(this.content)
    }
}

在对上面两种节点的改造下,我们尝试表示一下html:

img

image.png

let virtualDom = new VNode('div', {class: 'container'}, [
    new TextNode('some content'),
    new VNode('span', {}, [
        new TextNode('other content')
    ])
])

本文demo源码: https://github.com/boomler/virtual-dom-demos/tree/master/demo1

作者:浩神

链接:https://www.jianshu.com/p/2d24d8095d1c

来源:简书