为什么要有diff算法呢?因为在v-for渲染列表的时候,我们在更改数据的时候会进行整个列表的渲染,为了增加代码效率,我们只需要进行一个局部的渲染,所以我们要使用diff算法进行节点比对,进行局部渲染。
在我们了解diff算法前,我们需要知道什么是虚拟DOM
虚拟DOM :
虚拟DOM就是用来表示真实DOM的对象(通俗来讲就是以对象的形式去描述我们的真实DOM)
例子:
真实DOM:
<ul id="list">
<li class="item">雪碧</li>
<li class="item">可乐</li>
<li class="item">芬达</li>
</ul>
对应的虚拟DOM为:
let oldVDOM = { // 旧虚拟DOM
tagName: 'ul', // 标签名
props: { // 标签属性
id: 'list'
},
children: [ // 标签子节点
{
tagName: 'li', props: { class: 'item' }, children: ['雪碧']
},
{
tagName: 'li', props: { class: 'item' }, children: ['可乐']
},
{
tagName: 'li', props: { class: 'item' }, children: ['芬达']
},
]
}
当我们去修改真实DOM的时候,会产生一个新的虚拟DOM,而我们的diff算法就是新旧虚拟DOM的对比查看每个节点是否发生改变,如果改变那么就进行局部渲染 。
新旧虚拟DOM对比的时候,Diff算法比较只会在同层级进行, 不会跨层级比较。 所以Diff算法是:广度优先算法。(同层的新旧虚拟节点比较)
总共有五种比较情况:
1、oldS 和 newS
使用sameVnode方法
进行比较,sameVnode(oldS, newS)
2、oldS 和 newE
使用sameVnode方法
进行比较,sameVnode(oldS, newE)
3、oldE 和 newS
使用sameVnode方法
进行比较,sameVnode(oldE, newS)
4、oldE 和 newE
使用sameVnode方法
进行比较,sameVnode(oldE, newE)
5、如果以上逻辑都匹配不到,再把所有旧子节点的 key
做一个映射到旧节点下标的 key -> index
表,然后用新 vnode
的 key
去找出在旧节点中可以复用的位置。
注 :这里的key不能使用索引哦!
第一步:
oldS = a, oldE = c newS = b, newE = a
比较结果:oldS 和 newE
相等,需要把节点a
移动到newE
所对应的位置,也就是末尾,同时oldS++
,newE--
第二步:
oldS = b, oldE = c newS = b, newE = e
比较结果:oldS 和 newS
相等,需要把节点b
移动到newS
所对应的位置,同时oldS++
,newS++
第三步:
oldS = c, oldE = c newS = c, newE = e
比较结果:oldS、oldE 和 newS
相等,需要把节点c
移动到newS
所对应的位置,同时oldS++
,oldE--
,newS++
第四步:
oldS > oldE
,则oldCh
先遍历完成了,而newCh
还没遍历完,说明newCh比oldCh多
,所以需要将多出来的节点,插入到真实DOM上对应的位置上