虚拟 DOM 与 DOM diff

241 阅读3分钟

一、虚拟DOM

虚拟DOM是一个代表DOM树的对象,通常含有标签名,标签上的属性、事件监听和其子元素,以及其他属性。

以Vue为例,创建一个虚拟DOM

const vNode = {
  tag: "div", // 标签名 or 组件名
  data: {
    class: "red", // 标签上的属性
    on: {
      click: () => {} // 事件
    }
  },
  children: [ // 子元素们及其属性
    { tag: "span", ... },
    { tag: "span", ... }
  ],
  ...
}

二、虚拟DOM的优缺点

  • 优点

    1. 减少DOM的操作

      • 虚拟DOM可将多次操作合并为一次完成

        比如需要添加1000个节点时,真是DOM需要依次添加,但使用虚拟DOM可一次性将多个节点添加至页面。

      • 虚拟DOM可借助DOM diff减少多余操作

        比如需要添加1000个节点,但其中只有10个节点为新增节点。虚拟DOM可通过diff算法,实现只对10个节点的添加,而不改动其他节点。

    2. 实现跨平台渲染

      虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,不仅可以变成DOM,还可以变成安卓/ios应用,小程序。

  • 缺点

    1. 需要额外的创建函数来创建虚拟DOM

      如在react中使用createElement,在vue中使用h,前者可以使用JSX来简化成XML写法,后者可以使用template,但是严重依赖打包工具,前者依赖babel-loader,后者依赖vue-loader。

    2. 大规模DOM操作时虚拟DOM反而比真实DOM更慢

      在测试增加100000节点的情况下,使用react非常慢

三、DOM diff

  • DOM diff

    DOM diff是虚拟DOM的对比算法,就是一个函数,被称为patch。

    使用patch获取需要运行的DOM操作

    patches = patch(oldVNode, newVNode)
    //可能的内容形式
    [
    	{type: 'INSERT', vNode: ... },
    	{type: 'TEXT',  vNode: ... },
    	{type: 'PROPS', propsPatch: [...]}
    ]
    
  • DOM diff的不同概念

    • Tree diff

      将新旧两颗虚拟 DOM 树,按照层级对应的关系,从头到尾的遍历一遍,,就能找到那些元素是需要更新的。

    • Component diff

      比较同一层级中的组件,比较组件类型。

      若类型相同,比较组件属性。 若类型不同,则删除旧组件,插入新组件进行替换。

      比较后对组件内部进行Tree diff ---- 递归。

    • Element diff

      比较同一层级中的元素,比较标签名。

      若标签名相同,则比较标签属性。 若标签名不同,则直接进行替换。

      再对子元素进行Tree diff ---- 递归。

  • DOM diff的优点

    将新的DOM树和旧的DOM树进行比较,减少了DOM操作,只对变化了的部分进行渲染,大大提高了渲染效率

  • DOM diff的缺点

    同级节点对比存在识别错误的问题

    由于index是动态变化的,删除左侧元素,右侧元素index自动减一,导致结果错误

    解决方法:为同级子节点添加唯一key进行区分

    可参考:Vue2.0 v-for 中 :key 到底有什么用?