虚拟 DOM 与 DOM diff

348 阅读2分钟

虚拟 DOM

众所周知,虚拟 DOM 是框架对原生 DOM 操作的优化方法

在 Vue 中,我们一般这样创建节点

<div class='red' @click="onClick"></div>

React 则使用 JSX 语法来创建,但是本质上都是创建了一个对象,而不是我们所想的原生标签的样子,我们成为 VNode 对象

以 React 的虚拟 DOM 为例子

const VNode={
    props:{
        children:[
            {type:'span',...}
            {type:'span',...}
        ],
        className:'wrapper',
        onClick:()=>{}
    },
    ref:null,
    type:'div'
}

看吧,它长得并不像真实 DOM,只是我们可以依靠一些属性来判断它的信息

知道虚拟 DOM 的样子了,那么它到底有什么优点和缺点呢?

  1. 可以减少 DOM 操作

如果我们用原生 api 来添加很多节点,会拉低渲染的效率,虽然代码运行很快,但是可能需要等待,而虚拟 DOM 是批量合并 DOM 操作,一次性部署

其独有的 DOM diff 可以判断新旧虚拟 DOM 的变化,将渲染任务大大压缩,比如只是内容的变化,就只需要替换内容而不是新建节点

  1. 可以跨平台 虚拟 DOM 是一个对象,它可以变成小程序,或者安卓和 ios 的原生组件

缺点就是需要额外的创建函数,即 createElement 和 h

DOM diff

那么 diff 是如何决定节点该如何更新呢

进行同级对比,如果节点为原生标签,先看标签名,若标签名不同,则直接替换,如果相同,则更新其属性

如果节点为组件,先观察组件类型,若不同也直接替换,类型相同也只更新属性

但是即使是如此细致的对比,也会出现问题

我们都知道,在循环一个数组来创建节点时,需要 key 的存在

我曾做过实验,创建一些含有复选框的子元素,再写一个添加元素的功能

比如你先选中第一个元素,然后在数组的首位新增一个元素,此时,新元素的复选框被选中,而原来的元素的复选框则没有被选中,看起来就好像是新元素继承了原来元素的状态

这是因为没有加 key,diff 无法精确检测新旧对象的不同,所以新元素直接复用了原来元素的状态

所以,key 的作用就是,在我们更新了渲染列表后,能够让 DOM diff 精确判断新旧虚拟 DOM 的不同,从而实现精确渲染

最后要记得,key 最好是 id,而不是数组的下标,因为数组下标不唯一,可能会改变