1.什么是虚拟DOM?
是一个能代表DOM 树的对象,通常含有标签名、标签上的属性、事件监听和子元素们,以及其他属性
实际上虚拟DOM是与真实的DOM对应的一个概念,在以下两种情况下虚拟DOM比真实DOM快的,或者说有如下两种优点
- 可以减少DOM操作:虚恢DOM可以将多次操作合并为一次操作,比如你添加1000个节点,却是一个接一个操作的;虚拟DOM借助DOM diff可以把多余的操作省掉,比如你添加1000个节点,其实只有10个是新增的
- 跨平台:虚拟DOM不仅可以变成 DOM,还可以变成小程序、iOS应用、安卓应用,因为虚拟DOM本质上只是一个JS对象
虚拟DOM有缺点吗?
需要额外的创建函数,如 createElement 或 h,但可以通过JSX来简化成XML写法,不过有需要对外部插件的依赖。
结论:
当你的DOM数量规模在几千数量级的时候,使用虚拟DOM是很好的选择,它会很好的优化一些多余的操作,从而提高性能;但是数量级在万级别或者几十万级别的时候虚拟DOM并不会表现很好,甚至会因为自身的一些算法而变得时间过长,比如React
2.什么是DOM diff
假如把虚拟DOM想象成树形
<div :class='x'>
<span v-if='y'>{{string}}</span>
<span>{string2}</span>
</div>
当属性变化时,比如 class属性 从 x 变为 red,于是DOM diff发现该元素标签类型没变,只需要更新 div 对应的 DOM的属性即可,并且子元素没变,无需更新
当数据变化时,比如说 y 从true 变成 false时候,DOM diff 发现div没变,子元素1标签没变,但是children文字变了,更新DOM内容,删除span2
什么是DOM diff
就是一个函数,我们称之为 patch
patches = patch(oldVNode,newVNode)
changed 就是要运行的DOM操作,可能长这样:
[ {type:'INSERT',vNode:...}, {type:'TEXT',vNode:...}, {type:'PROPS',propsPatch:...}, ]
DOM diff 大致逻辑
- tree diff:
- 将新旧两棵树逐层去对比,找出哪些节点需要更新
- 如果节点是组件就看Component diff
- 如果节点是标签就看Element diff
- Component diff
- 如果节点是组件,就先看组件类型
- 类型不同直接替换(删除日的)
- 类型相同则只更新属性
- 然后深入组件做Tree diff(递归)
- Element diff
- 如果节点是原生标签,则看标签名
- 标签名不同直接替换,相同则只更新属性
- 然后进入标签后代做Tree diff(递归)
DOM diff 的缺点
同级节点对比存在bug,会出现识别错误的问题
这里便引出了一个经典问题---Vue2.0 v-for 中 :key 到底有什么用?
这里参考方应杭的文章https://www.zhihu.com/question/61064119/answer/766607894
两篇深入的阅读
React虚拟Dom和diff 算法:https://juejin.cn/post/6844903529161850893
React 源码剖析系列-不可思议的react diff:https://zhuanlan.zhihu.com/p/20346379