一、虚拟DOM
虚拟DOM是一个代表DOM树的对象,通常含有标签名,标签上的属性、事件监听和其子元素,以及其他属性。
以Vue为例,创建一个虚拟DOM
const vNode = {
tag: "div", // 标签名 or 组件名
data: {
class: "red", // 标签上的属性
on: {
click: () => {} // 事件
}
},
children: [ // 子元素们及其属性
{ tag: "span", ... },
{ tag: "span", ... }
],
...
}
二、虚拟DOM的优缺点
-
优点
-
减少DOM的操作
-
虚拟DOM可将多次操作合并为一次完成
比如需要添加1000个节点时,真是DOM需要依次添加,但使用虚拟DOM可一次性将多个节点添加至页面。
-
虚拟DOM可借助DOM diff减少多余操作
比如需要添加1000个节点,但其中只有10个节点为新增节点。虚拟DOM可通过diff算法,实现只对10个节点的添加,而不改动其他节点。
-
-
实现跨平台渲染
虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,不仅可以变成DOM,还可以变成安卓/ios应用,小程序。
-
-
缺点
-
需要额外的创建函数来创建虚拟DOM
如在react中使用createElement,在vue中使用h,前者可以使用JSX来简化成XML写法,后者可以使用template,但是严重依赖打包工具,前者依赖babel-loader,后者依赖vue-loader。
-
大规模DOM操作时虚拟DOM反而比真实DOM更慢
在测试增加100000节点的情况下,使用react非常慢
-
三、DOM diff
-
DOM diff
DOM diff是虚拟DOM的对比算法,就是一个函数,被称为patch。
使用patch获取需要运行的DOM操作
patches = patch(oldVNode, newVNode) //可能的内容形式 [ {type: 'INSERT', vNode: ... }, {type: 'TEXT', vNode: ... }, {type: 'PROPS', propsPatch: [...]} ] -
DOM diff的不同概念
-
Tree diff
将新旧两颗虚拟 DOM 树,按照层级对应的关系,从头到尾的遍历一遍,,就能找到那些元素是需要更新的。
-
Component diff
比较同一层级中的组件,比较组件类型。
若类型相同,比较组件属性。 若类型不同,则删除旧组件,插入新组件进行替换。
比较后对组件内部进行Tree diff ---- 递归。
-
Element diff
比较同一层级中的元素,比较标签名。
若标签名相同,则比较标签属性。 若标签名不同,则直接进行替换。
再对子元素进行Tree diff ---- 递归。
-
-
DOM diff的优点
将新的DOM树和旧的DOM树进行比较,减少了DOM操作,只对变化了的部分进行渲染,大大提高了渲染效率
-
DOM diff的缺点
同级节点对比存在识别错误的问题
由于index是动态变化的,删除左侧元素,右侧元素index自动减一,导致结果错误
解决方法:为同级子节点添加唯一key进行区分