在了解diff算法前,我们需要想了解一下什么是虚拟DOM,diff算法为什么可以提升代码效率?
一、什么是虚拟DOM
虚拟DOM
是一个对象
,一个什么样的对象呢?一个用来表示真实DOM的对象。我举个例子,请看以下真实DOM
:
<!-- 真实DOM -->
<ul id="list">
<li class="item">桃子</li>
<li class="item">苹果</li>
<li class="item">西瓜</li>
</ul>
生成一个虚拟DOM:
<!-- 虚拟DOM:对象的形式描述真实DOM -->
let oldVDOM = {
tagName: 'ul', // 头标签名
props: { // 头标签的属性
id: 'list'
}
children: [ // 标签的子节点,上面真实DOM的子节点有三个
{
// 对子节点进行描述
tagName: 'li', progs: { class: 'item'}, children: '桃子'
},
{
tagName: 'li', progs: { class: 'item'}, children: '苹果'
},
{
tagName: 'li', progs: { class: 'item'}, children: '西瓜'
}
]
}
在修改一个li标签的文本时,生成一个新虚拟DOM,来表示真实的修改后的DOM
let newVDOM = {
tagName: 'ul', // 头标签名
props: { // 头标签的属性
id: 'list'
}
children: [ // 标签的子节点,上面真实DOM的子节点有三个
{
// 对子节点进行描述
tagName: 'li', progs: { class: 'item'}, children: '桃子'
},
{
tagName: 'li', progs: { class: 'item'}, children: '西瓜'
},
{
tagName: 'li', progs: { class: 'item'}, children: '苹果'
},
{
tagName: 'li', progs: { class: 'item'}, children: '草莓'
}
]
}
这就是我们的新旧虚拟DOM,在我们修改li时,有两种渲染列表的方法,
①直接渲染
②使用虚拟DOM
虚拟DOM算法操作真实DOM,性能高于直接操作真实DOM。
虚拟DOM
和虚拟DOM算法
是两种概念。虚拟DOM算法 = 虚拟DOM + Diff算法
二、什么是Diff算法
Diff算法是一种对比算法。对比两者是旧虚拟DOM和新虚拟DOM
,对比出是哪个虚拟节点
更改了,找出这个虚拟节点
,并只更新这个虚拟节点所对应的真实节点
,而不用更新其他数据没发生改变的节点,实现精准
地更新真实DOM,进而提高效率
。
三、Diff算法的原理:
①Diff同层对比
新旧虚拟DOM对比的时候,Diff算法比较只会在同层级进行, 不会跨层级比较。 所以Diff算法是:广度优先算法
。 时间复杂度:O(n)
②比较的东西
新旧虚拟DOM对比的时候,首先比较的key
③新旧DOM比较
那么新旧两个子节点集合以及其首尾指针为:
然后会进行互相进行比较,总共有五种比较情况:
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
去找出在旧节点中可以复用的位置。
具体步骤:
第一步:olds 和 news 比较
第二步:olds++ 指针指向苹果 news ++ 指针指向 西瓜
后面是相同的比较方法
注意: (在我们渲染列表的时候,需要绑定key.)
绑定key我们第一时间肯定会想让我们的索引成为key,但是这是错误的
我们在比较新旧虚拟DOM的时候,第一步比较的就是他们的key值,如果使用索引充当key的话。没有我们想着的,复用已有节点,局部渲染不同节点或新增节点等。
所以,我们key需要使用唯一确定的值,去充当key值。