什么是虚拟DOM?有什么用?
虚拟DOM是对真实DOM的抽象,本质上是JavaScript对象,这个对象就是更加轻量级的对DOM的描述。
首先都知道频繁操作变动真实DOM会引起浏览器的重绘和回流,这会大大降低浏览器的性能,而在 前端性能优化的其中有一个方向就是尽可能操作真实DOM,从而达到提升性能的目的。因为紧紧修改虚拟dom中的内容,不会带来页面的重绘和回流操作。在完成虚拟DOM修改后,进行真实DOM的修改,才会使页面重绘。因此我们需要这层抽象,在path(绘制)过程中尽可能一次性将差异更新到DOM中,这样在一定程度上保证了DOM的性能不会很差。
最后,也是虚拟DOM最初的目的,就是更好的跨平台,比如Node.js就没有DOM,如果想实现SSR(服务端渲染),那么一个方式就是借助虚拟DOM,因为虚拟DOM本身是JavaScript对象。
虚拟DOM的解析过程
- 构建虚拟dom树:开发者编写的模板或组件代码被编译器或解析器解析为虚拟dom树。虚拟dom是一个纯JavaScript对象,描述整个页面的结构和内容。
- 初次渲染:首次渲染时,将虚拟dom树转换为真实的dom树,并插入页面中。这一过程也称为“挂载”。
- 更新虚拟dom:当页面需要更新时,开发者会使用JavaScript代码修改dom树。在这衣蛾过长,不会直接修改真实的DOM。
- 比较虚拟DOM:比较修改前后的DOM树,找出需要更新的部分。
- 更新真实DOM:根据比较结果,只更新需要更新的真实DOM。这个过程称为重新渲染。
当页面的状态发生改变或需要对页面的DOM树结构进行修改的时候,首先会根据变更的状态重新构建起一颗DOM树,然后将新的DOM树和旧的DOM进行比较,通过diff 算法记录两颗树的差异。最后的差异部分应用到真实的DOM中去,这样视图就会更新。
diff 算法
diff算法是一种对同层DOM节点进行比较的高效算法,传统diff算法通过循环递归对节点进行依次比较,效率低下,算法复杂度达到O(n^3), 而改进后的diff算法避免了对DOM树进行逐层搜索遍历,所以时间复杂度只有O(n)。diff算法在很多场景下都有应用,例如,Vue虚拟DOM渲染成DOM的新旧虚拟DOM节点比较更新时,就用到该算法。
diff算法特点:不会跨层级比较,只会在同层级进行比较,
diff算法的基本原理有以下几点:
- 对于同层级的节点首先对比新旧节点的头尾、头与头、尾与尾分别进行对比,寻找未移动的节点。
- 新旧节点头尾对比完后,进行头与尾,尾与头的交叉对比,这一步的目的是寻找可复用的节点。
- 在交叉对比结束后,因为有可能还有可复用的节点,所以创建一个老节点letToIndex的哈希表map记录key,然后继续遍历信号节点索引通过key查找客服用的旧节点。
- 节点完成便利后,通过新老索引,移除多余纠结点或者增加新节点。
Vue中key的作用:
- 为了性能优化:因为vue底层是虚拟DOM,更新DOM时用diff算法对新旧节点对比,找到客服用的节点,减少操作真实DOM的次数。在更新数据渲染列表时,如果数据的顺序发生了改变,Vue不会移动DOM元素来匹配数据的顺序,而是采用“就地复用”策略,简单地复用此处的每个元素,因此通过每个节点设置一个key值,以便Vue判断节点是否可以服用,从而高效的实现更新渲染虚拟DOM.
- 确保正确的组件状态
在使用带有状态的组件时,如果不适用key,可能会出现状态错乱的问题。使用key可以确保Vue对每个节点都能正确地保存组件状态。