一:虚拟DOM
虚拟DOM
模仿了实际的 DOM
节点,并包含有关节点的信息,例如标签名、属性、子节点等。
简单举个例子:
js
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
Obj = { // 虚拟dom结构, 根据虚拟dom生成真实dom
tag: 'ul',
children: [
{tag: 'li', children: [{vnode: {text: '1'}}]},
{tag: 'li', children: [{vnode: {text: '2'}}]},
]
}
在运行时,会把代码编译成一个对象,也就是虚拟 DOM
,然后生成真实的 DOM
,但在实际中,Vue 使用了一种更加复杂的对象结构来存储虚拟节点(VNode
),并且包含了更多的元信息,比如节点的类型、属性、事件监听器等等。
二:diff
算法
在DOM
结构发生改变时,会生成一个新的虚拟DOM
,此时就需要一个算法来比较新旧DOM
的不同来更快速的渲染,也就是diff
算法。
比较过程中,会根据虚拟DOM
树的结构,一层一层比较,首先比较的是两个虚拟 DOM
的根节点是否相同,如果根节点相同才会继续检查子节点,看是否有可复用的DOM
,否则不会再继续比较子节点。
那么会有一个疑问了,如果两个虚拟
DOM
只有根节点不同,子节点完全一样呢?确实,这样的两个新旧虚拟DOM
树可复用高,但是如果diff
算法改成了不依据根节点是否相同来确定是否要检查子节点,那么会对逐一对比子节点找可复用部分,加大了引擎压力,有点捡了芝麻丢了西瓜那味了
2.1.判断两个跟节点是不是相同
- 判断两个节点上的key是否相同
- 判断两个节点的标签名是否相同
- 标签上的属性
2.2.diff比较过程
- 旧首节点,新首节点,旧尾结点,新尾结点
- 不是相同的节点,直接销毁
oldVnode
,启用newVnode
newVnode
是不是文字节点 (直接让浏览器修改文字)- 有新的
children
没有旧children
(记录新增) - 没有新的
children
有旧children
(记录删除) - 有新的
children
有旧children
(diff
核心工作 两两节点比较)
三:diff
中的key
3.1.key为什么不能是index?
diff
中判断两个节点是否相同首先判断的就是两个节点的key
是否相同, 如果用index
作为key
,index
不会随着元素位置的变更而移动,从而导致相同可复用的节点被认为不相同,降低了DOM的复用性,浪费性能
3.2.可不可以用随机数做key?
当然不可以,和index
的原因是一样的,但比index
效率更低下。用随机数的话新旧树的每一个DOM结构都是不同的key
,用index
,还可能有些DOM结构在新树中index
没变,然后得以复用。
四:虚拟DOM的好处
想必友友们在其他文章中已经看过无数遍这个问题的答案了-->因为虚拟DOM性能好。
其实不然,虚拟DOM实际就是把HTML读成JS对象,再生成HTML结构,用虚拟DOM性能并不会有所提高,会开销很多v8的性能。一个新的前端框架svelte
就是主打的无虚拟DOM,此外连尤雨溪自己都说在VUE4会把虚拟DOM砍掉。
优点:
- 跨平台:支持构建可以在多个平台上运行的应用程序,虚拟 DOM 允许开发者使用一套统一的 API(JavaScript)去描述用户界面,而不需要关心底层的具体实现细节,这在一定程度上促进了跨平台开发。
- 分摊了浏览器渲染线程的压力,减少了回流重绘 :当数据发生变化时,
diff
算法能够计算出最小化的 DOM 更新操作,这样只需要对实际发生变化的部分进行更新。避免了不必要的 DOM 操作,从而减少了浏览器的回流和重绘,提高页面的响应速度和流畅度。