虚拟 DOM 和 DOM diff

156 阅读3分钟

虚拟DOM是什么

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

  • Vue中的虚拟DOM
const vNode = {
  tag: "div", // 标签/组件名
  data: {
    class: "child", // 标签上的属性
    on: {
      click: fn // 事件
    }
  },
  children: [ // 子元素
    { tag: "div", ... },
    { tag: "span", ... }
  ],
  ...
}
  • React中的虚拟DOM
const vNode = {
  key: null,
  props: {
    children: [  // 子元素
       { type: 'div', ... }, 
       { type: 'span', ... }
    ],
    className: "child" // 标签上的属性
    onClick: fn // 事件
  },
  ref: null,
  type: "div", // 标签/组件名
  ...
}

上面是Vue和React中的虚拟DOM的样子。那么,我们如何才能创建虚拟DOM呢?

在Vue中,我们可以使用h函数

const App = new Vue({
      render(h=>{
      h('div', {
      class: 'child',
      on: {
        click: fn
      },
    }, [h('div',{},'div1'), h('span', {}, 'span1'])
  })
})

在React中,我们可以使用React.createElement

const App = React.createElement(
  'div',{className:'red',onClick:()=> {}},[
     createElement('span', {}, 'span1'),
     createElement('span', {}, 'span2')
   ]
)

虚拟DOM的优点

  1. 能减少不必要的DOM操作
  • 次数方面 例如我们需要在页面中创建1000个节点,那么真实DOM需要一个一个的去创建,而虚拟DOM可以借助DOM diff将其合并为一次操作

  • 范围方面 还是创建1000个节点,如果这1000个节点中只有20个是新增的,那么通过虚拟DOM和DOM diff ,就只需要增加那多出来的20个节点就可以了。

  1. 跨平台 虚拟DOM的本质是一个js对象,所以虚拟DOM不仅可以用于web,还能用于ios,安卓,小程序等地方。

虚拟DOM的缺点

上面我们提到了,要创建虚拟DOM需要用到h函数或者React.createElement函数,而且,为了方便书写,我们会用jsx或者.vue文件来书写虚拟节点。

这样的话,使用虚拟节点就需要额外的创建函数,打包时需要额外的babel/loader。

DOM diff 是什么

上面我们多次提到了 DOM diff 那么,DOM diff到底是什么呢?

DOM diff 是虚拟DOM的对比算法。

DOM diff会将虚拟DOM树进行层层对比,首先对比标签名/组件名,然后是属性,然后是内容,然后计算出已经变化的地方,最后返回一个新的标签/组件。

DOM diff 的问题

在DOM diff 中,有一个关键的问题,那就是计算机的比对和我们人类的比对不太一样。

比如下面有两个数组

const array1 = [1,2,3]
const array2 = [1,3]

对于DOM diff来说,在比对 array[0] 时,发现都是1,没有变化,

在对比 array[1] 时,发现2变成了3,所以dom diff会将 原先的2变为3,

而在对比 array[2] 时,发现 一个是3,一个是undefined,所以会将3直接删除。

这就会导致 DOM diff 的效率变慢,解决办法是添加一个key,这样的话当DOM diff 对比两个数组时就会发现,数组只是减少了一项,而不再需要像上面那样去繁琐的对比。