对Diff算法的认识
概念
diff算法是一种优化手段,将前后两个模块进行差异对比,修补(更新)差异的过程叫做patch(打补丁)
为什么vue,react这些框架中都会有diff算法呢? 我们都知道渲染真实dom的开销是很大的,这个跟性能优化中的重绘重排意义类似, 回到正题来, 有时候我们修改了页面中的某个数据,如果直接渲染到真实DOM中会引起整棵数的重绘重排, 那么我们能不能只让我们修改的数据映射到真实DOM, 做一个最少化重绘重排呢,说到这里你应该对为什么使用diff算法有一个简单的概念了
# virtual DOM和真实DOM的区别
一句话概括吧,virtual DOM是将真实的DOM的数据抽取出来,以对象的形式模拟树形结构, diff 算法比较的也是virtual DOM
<div>
<p>
JS每日一题
</p>
</div>
// 转换成VNode 类似于下面这种
const Vnode = {
tag: 'div',
children: [
{ tag: 'p', text: 'JS每日一题' }
]
};
2
3
4
5
6
7
8
9
10
11
12
13
# diff是怎么比较的
简单的说就是新旧虚拟dom 的比较,如果有差异就以新的为准,然后再插入的真实的dom中,重新渲染
特点
只会做同级比较,不做跨级比较
比较后几种情况
if(oldVnode===vnode),他们的引用一致,可以认为没有变化。
if(oldVnode.text!==null&&vnode.text!==null&&oldVnode.text!==vnode.text),文本节点的比较,需要修改,则会调用 Node.textContent=vnode.text。
if(oldCh&&ch&&oldCh!==ch), 两个节点都有子节点,而且它们不一样,这样我们会调用 updateChildren函数比较子节点,这是diff的核心
elseif(ch),只有新的节点有子节点,调用 createEle(vnode), vnode.el已经引用了老的dom节点, createEle函数会在老dom节点上添加子节点。
elseif(oldCh),新节点没有子节点,老节点有子节点,直接删除老节点。
# key的作用
设置key和不设置key的区别:
不设key,newCh和oldCh只会进行头尾两端的相互比较,设key后,除了头尾两端的比较外,还会从用key生成的对象 oldKeyToIdx中查找匹配的节点,所以为节点设置key可以更高效的利用dom
如我们希望可以在B和C之间加一个F,Diff算法默认执行起来是这样的:
即把C更新成F,D更新成C,E更新成D,最后再插入E,是不是很没有效率?
所以我们需要使用key来给每个节点做一个唯一标识,Diff算法就可以正确的识别此节点,找到正确的位置区插入新的节点。
所以一句话,key的作用主要是为了高效的更新虚拟DOM。 另外vue中在使用相同标签名元素的过渡切换时,也会使用到key属性,其目的也是为了让vue可以区分它们,否则vue只会替换其内部属性而不会触发过渡效果
# 总结
尽量不要跨层级的修改dom
在开发组件时,保持稳定的 DOM 结构会有助于性能的提升
设置key可以让diff更高效
# 参考文章链接
15 张图,20 分钟吃透 Diff 算法核心原理,我说的!!! (opens new window)
diff 算法深入一下? (opens new window)
深入剖析:Vue核心之虚拟DOM (opens new window)