虚拟 DOM 和 DOM diff

130 阅读3分钟

虚拟 DOM 是什么

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

  • 图示

虚拟 DOM 的优点

  • 减少DOM操作

    虚拟DOM可以将多次操作合并为一次操作,比如添加1000个节点,基于真实DOM的原生JS是一个接一个操作,将1000个节点依次插入到页面上,而虚拟 DOM 则是会先用JS对象模拟 DOM 树,先在该对象上执行 1000 次添加节点的操作,然后将这个虚拟 DOM 一次性的插入到页面上。

    虚拟 DOM 借助 DOM diff 可以把多余的操作省掉,缩小DOM操作的范围。比如你添加 1000 个节点,其实页面上已经存在了前 9990 个节点了,只有 10 个是新增的,基于真实 DOM 的原生 JavaScript 会先将原来的 9990 个节点删掉,然后再将这 10000 个节点添加到页面上。而虚拟 DOM 可以借助 DOM diff 算法,对比两棵 DOM 树的不同点,得到最小更新的 patch(就是那 10 个节点),只将这 10 个新节点插入到页面上即可。

  • 跨平台

    虚拟 DOM 不仅可以变成真实 DOM,还可以变成小程序、iOS 应用、安卓应用,因为虚拟 DOM 本质上只是一个 JS 对象。

虚拟 DOM 的缺点

需要额外的创建函数,如createElement或h,但可以通过JSX来简化成XML写法,严重依赖打包工具

DOM diff 是什么

  • 虚拟DOM的对比算法,就是一个函数,我们称之为patch
  • patches = patch(oldVNode,newVNode)
  • patches就是要运行的DOM操作,可能长这样:
[
	{type:'INSERT',vNode:...}
    {type:'TEXT',vNode:...}
    {type:'PROPS',propsPatch:[...]}
    
]
  • 图示

DOM diff 的优点

DOM diff算法就是在虚拟DOM树从上至下进行同层比对,如果上层已经不同了,那么下面的DOM全部重新渲染。这样的好处是算法简单,减少比对次数,加快算法完成速度。

DOM diff 的问题

同级节点对比存在bug,以上图为例既然是删除第一个 span,为什么不直接删除呢?反而还浪费第二次 diff 的性能。

有问题就会有解决方式,Vue 和 React 都建议在执行列表渲染时给每个组件添加上 key 这个属性,这个 key 属性就是节点的唯一标识,有了这个标识再看刚才的例子,DOM diff 时发现第一个 span 的 key 不见了,就直接删掉第一个 span 元素即可。

有了这个 key,就会使 DOM diff 的过程变得更加高效,但是在使用的时候,这个 key 也是有注意点的: key 的值必须使一个唯一的且不可变的值,否则在某些情况下会出问题的。

key 到底有什么用可以参考www.zhihu.com/question/61…

在列表渲染时,key 值的绑定对象最好不要使用 index。

为什么不能用 index 作为 key,如果你用 index 作为 key,那么在删除第二项的时候,index 就会从 1 2 3 变成 1 2(而不是 1 3),那么 Vue 依然会认为你删除的是第三项。也就是会遇到上面一样的 bug。

所以,永远不要用 index 作为 key。永远不要!