虚拟 DOM 和 DOM diff

217 阅读2分钟

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

虚拟 DOM 有两个优点。

第一个优点是虚拟 DOM 可以将多次 DOM 操作合并为一次操作,也可以通过 DOM diff 将多余的 DOM 操作省略掉,从而提高渲染的效率。

第二个优点是虚拟 DOM 可以跨平台。虚拟 DOM 不仅可以变成 DOM,还可以变成小程序、iOS 应用、安卓应用,这是因为虚拟 DOM 本质上只是一个 JS 对象,所以可以进行跨平台。

那么虚拟 DOM 有没有什么缺点呢?

当然是有的。虚拟 DOM 需要额外的创建函数,在 React 中需要使用 create Element,在 Vue 中则需要使用 h 函数,但是我们可以通过 JSX 来将其简化为 XML 的写法。

在极限情况下,例如通过虚拟 DOM 添加十万个节点(当然这种情况比较少见,一般一个页面的节点在两千到一万之间),原生 DOM 和 Vue 大概需要 10s,而 React 则需要 30s。

如果你在使用 React 时要添加大量节点,建议直接使用虚拟 DOM 而不是 createElement 来提升效率。

如果只是添加一些节点的话使用 createElement 反而可能会更快一些。

虚拟 DOM 最大的用处是用来 DOM diff。那么 DOM diff 又是什么呢?

DOM diff 本质上可以看作一个函数,这个函数的作用就是将旧的 DOM 树和新的 DOM 树逐层进行对比,找出哪些节点需要更新,然后进行对应的操作(增删改)。

通过 DOM diff,页面可以只替换发生改变的位置而不用整个页面进行重新渲染,大大提升了效率。但是 DOM diff 有一个 bug。

假如现在页面中有三个节点,他们的名称为a,b,c,他们的下标为 0,1,2。当我们删除第二个节点既 b 时,页面剩下的内容会是 a,b,下标为 0,2。这和你预料的是不是不太相同。

会出现这种情况的原因是 DOM diff 对比两棵树之后,执行的操作是先把下标 1 变为下标 2,再将原来下标 2 的内容删除掉。

这个 bug 的解决方法很简单,就是你给节点添加一个 key,注意这个 key 不能为节点的下标,否则还是会出现上面的 bug。