虚拟DOM

361 阅读2分钟

Virtual DOM

虚拟DOM是什么?

直白的说,一个虚拟DOM就是一个JS对象,编译阶段通过固定的逻辑(不同框架对虚拟DOM的解析方法不同),将这个对象解析,并生成真实的DOM节点到页面上。

虚拟DOM最显而易见的优势是,使用DOM diff算法将js操作大量DOM节点的时间损耗优化,一次修改所有产生变化的节点。另一方面,虚拟DOM对跨平台开发成为可能,虚拟DOM的本质就是一个js对象,使用不同的解析方式甚至可以把虚拟DOM解析为安卓和IOS的原生组件。

下面举个例子:

<div>
    <p class="text">这里是一段话</p>
</div>

转化为虚拟DOM可能是下面这样:

{
    tag: 'div',
    children: [
        {
            tag: 'p',
            props: {
                class: 'text'
            },
            children: [
                {text: '这里是一段话'}
            ]
        }
    ]
}

虚拟DOM的优缺点

虚拟DOM的优点在于DOM diff(减少DOM操作)和跨平台

  • 减少DOM操作

    例如:

    // 以下伪代码
    // 用DOM API生成1000个div
    for (let i = 1; i <= 1000; i++) {
        const div = document.createElement('div')
        div.innerText = i
        document.querySelector('#app').append(div)
    }
    // 以上方法是一个一个生成div元素,又一个一个把div append到页面里
    
    // 使用虚拟DOM的话,会一次性生成1000个虚拟DOM,然后把这1000个节点一次性生成为1000个div节点,并一次性加载到页面中
    // Vue
    // <div v-for="item in 1000">{{item}}</div>
    // React
    // return (
    //     {[1,...,1000].map(item => <div>{item}</div>)}
    // )
    
  • 跨平台

    跨平台的优点就如上述所说,虚拟DOM的本质就是一个js对象,使用不同的解析方式甚至可以把虚拟DOM解析为安卓和IOS的原生组件。

虚拟DOM的缺点

虚拟DOM的缺点限于需要特定的解析函数,例如Vue的h()和React的createElement()。如果难点算一个缺点,就是新手使用时因为不够了解DOM diff的特点,所导致的删除时出错。

DOM diff

DOM是树形结构,又因为虚拟DOM是js对象的本质,虚拟DOM同样可以转化为树结构。

而DOM diff,顾名思义,就是对虚拟DOM进行对比的算法(一般来说是一个patch函数)。通过对比新旧虚拟DOM树的每一个对象节点,每次找出变化的节点,把该节点的变化记录下来,直到遍历完整棵树,再仅把记录下的产生变化的节点重新渲染到页面,节省大量资源。

diff的过程及过程中的bug(key的重要性)

如下代码:

<div>
    <span v-for="item in ['1','2','3']">
        {{item}}
        <button>删除</button><!-- 删除当前span -->
    </span>
</div>

生成的虚拟DOM树为:

这时删掉第二个span,我们获得的虚拟DOM树为:

但diff算法实际上进行的操作为,删掉第三个span,将第二个span的值从2改为3:

有没有发现bug?

这时我们需要借助key,来标识每一个独特的虚拟DOM节点:

这时我们删除第二个span,diff算法发现删掉的确确实实是key=2的span节点:

如果需要key来标识不可复用节点,那么key同样不要使用index作为标识,一定要使用类似于id这样不会改变局部唯一的变量。