虚拟DOM和dom diff

1,604 阅读5分钟

什么是虚拟DOM

虚拟DOM是一颗以JavaScript对象(node节点)作为基础的树,用对象属性来描述节点,他是对真实DOM的抽象,通过一些列操作使这棵树映射到真实环境上

虚拟dom就是能代表DOM树的对象,通常含有标签名,标签上的属性,事件监听和子元素们以及其它属性

为什么要有虚拟DOM这个东西

人们创造出虚拟 DOM 就是为了更好地将虚拟节点渲染到视图上,也就是把虚拟DOM变成真实的 DOM 节点,提高视图的渲染性能。

** 在真实DOM操作中 **

传统的开发模式,原生JS或JQ操作DOM时,浏览器会从构建DOM树开始从头到尾执行一遍流程。在一次操作中,我需要更新10个DOM节点,浏览器收到第一个DOM请求后并不知道还有9次更新操作,因此会马上执行流程,最终执行10次。例如,第一次计算完,紧接着下一个DOM更新请求,这个节点的坐标值就变了,前一次计算为无用功。计算DOM节点坐标值等都是白白浪费的性能。操作DOM的代价是昂贵的,频繁操作还是会出现页面卡顿,影响用户体验。

** 使用虚拟DOM **

虚拟DOM就是为了解决浏览器性能问题而被设计出来的。若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的内容保存到本地一个JS对象中,最终将这个JS对象一次性的插入到DOM树上,再进行后续操作,避免大量无谓的计算量。所以,用JS对象模拟DOM节点的好处是,页面的更新可以先全部反映在JS对象(虚拟DOM)上,操作内存中的JS对象的速度显然要更快,等更新完成后,再将最终的JS对象映射成真实的DOM,交由浏览器去绘制。

虚拟DOM的优点(为什么虚拟DOM比DOM快)

1.减少DOM的操作:

虚拟dom可以将多次操作合并为一次操作,减少DOM操作的次数。比如你添加1000个节点,确实一个接一个操作, 虚拟DOM借助DOM diff可以把多余的操作省掉,减少DOM操作的范围。比如你添加1000个节点,其实只有10个是新增的

2.跨平台渲染:

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

虚拟DOM的缺点

需要额外的创建函数,如create Element或h,但是可以通过JSX来简化成XML写法

DOM diff

DOM diff 是两个虚拟 DOM 树对比的算法:diff 算法仅在两个树的同级的虚拟节点之间做比较,递归地进行比较,最终实现整个 DOM 树的更新。

diff算法的三个步骤:

  1. 用 JS 对象的方式来表示 DOM 树的结构,然后根据这个对象构建出真实的 DOM 树,插到文档中
  2. 当状态变化的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树的差异
  3. 最后把所记录的差异应用到所构建的真正的DOM树上,进行视图更新

DOM diff 在做比较时分为了三个层级

1.Tree Diff(层级比较)

  • 先进行树结构的层级比较,对同一个父节点下的所有子节点进行比较;
  • 接着看节点是什么类型的,是组件就做 Component Diff;
  • 如果节点是标签或者元素,就做 Element Diff;

2. omponent Diff (组件比较)

  • 若组件类型相同,则继续按照层级比较其虚拟 DOM的结构;
  • 如果组件类型不同,则替换整个组件的所有内容

3. Element Diff (元素比较)

  • 如果节点是原生标签,则看标签名做比较是否相同来决定替换还是更新属性

  • 然后进入标签后代递归 Tree Diff

DOM diff的问题

同级比较存在bug,做元素比较的时候,当 DOM 处于同一层级的时候,diff 提供了三个节点操作,分别是:删除、插入和移动

key值的问题

没有使用key的时候,你想把第二个方块删除掉,你想要到的结果是剩下三角和圆圈,但是真实的结果却是剩下三角和方框

这是因为,vue做了两件事

*  把2变成3

*  把3删除掉

这有两个数组[1,2,3] 和 [1,3]。

人类会说,这不就是少了个2嘛

但是计算机不会直接删除第二个,而是会遍历数组:

首先对比11,发现[1没变」;然后对比23,发现「2变成了3」;最后对比undefined3,发现[3被删除了」。
所以计算机的结论是:「2变成了 3」以及「3被删除了」

解决方法:

就是给每一个元素一个id,用id做key值

**为什么不能用index作为key **

如果你用index作为key,那么在删除第二项的时候,index 就会从1 23变成1 2(因为index永远都是连续的,所以不可能是1 3),

那么 Vue依然会认为你删除的是第三项。也就是会遇到上面一样的bug。

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