key和diff算法
来自课程:https://ke.qq.com/course/3453141
1.1. 认识VNode和VDOM
在使用v-for进行列表渲染时,我们通常会给元素或者组件绑定一个key属性。
这个key属性有什么作用呢?我们先来看一下官方的解释:
- key属性主要用在Vue的虚拟DOM算法,在新旧nodes对比时辨识VNodes;
- 如果不使用key,Vue会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法;
- 而使用key时,它会基于key的变化重新排列元素顺序,并且会移除/销毁key不存在的元素;
官方的解释对于初学者来说并不好理解,比如下面的问题:
- 什么是新旧nodes,什么是VNode?
- 没有key的时候,如何尝试修改和复用的?
- 有key的时候,如何基于key重新排列的?
我们先来解释一下VNode的概念:
- VNode的全称是Virtual Node,也就是虚拟节点;
- 事实上,无论是组件还是元素,它们最终在Vue中表示出来的都是一个个VNode;
- VNode的本质是一个JavaScript的对象;
<div class="title" style="font-size: 30px; color: red;">哈哈哈</div>
在我们的Vue中会被转化创建出一个VNode对象:
const vnode = {
type: 'div',
props: {
'class': 'title',
style: { 'font-size': '30px', color: 'red' }
},
children: '哈哈哈'
}
Vue内部在拿到vnode对象后,会对vnode进行处理,渲染成真实的DOM。
DOM渲染过程:
如果我们不只是一个简单的div,而是有一大堆的元素,那么它们应该会形成一个VNode Tree:
<div>
<p>
<i>哈哈哈哈</i>
<i>哈哈哈哈</i>
</p>
<span>嘻嘻嘻嘻</span>
<strong>呵呵呵呵</strong>
</div>
这个和我们的key有什么关系呢?
- 接下来我们就进行具体的分析;
1.2. key作用和diff算法
我们先来看一个例子:
- 这个例子是当我点击按钮时会在中间插入一个f;
<div id="app"></div>
<template id="my-app">
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
<button @click="insertF">insert f</button>
</template>
<script src="../js/vue.js"></script>
<script>
const App = {
template: "#my-app",
data() {
return { letters: ["a", "b", "c", "d"] };
},
methods: {
insertF() {
this.letters.splice(2, 0, "f");
},
},
};
Vue.createApp(App).mount("#app");
</script>
我们可以确定的是,这次更新对于ul和button是不需要进行更新,需要更新的是我们li的列表:
- 在Vue中,更新前后都相同的元素节点并不会重新渲染;
- 对于列表中 a、b、c、d它们都是没有变化的;
- 在操作真实DOM的时候,我们只需要在中间插入一个f的li即可;
那么Vue中对于列表的更新究竟是如何操作的呢?
- Vue事实上会对于有key和没有key会调用两个不同的方法;
- 有key,那么就使用
patchKeyedChildren
方法; - 没有key,那么就使用
patchUnkeyedChildren
方法;
1.3. 没有key执行操作
没有key对应的源代码如下:
它的过程画图就是如下的操作:
我们会发现上面的diff算法效率并不高:
- c和d来说它们事实上并不需要有任何的改动;
- 但是因为我们的c被f所使用了,所有后续所有的内容都要一次进行改动,并且最后进行新增;
1.4. 有key执行操作
如果有key,那么会执行什么样的操作呢?
第一步的操作是从头开始进行遍历、比较:
- a和b是一致的会继续进行比较;
- c和f因为key不一致,所以就会break跳出循环;
第一步对比的过程
第二步的操作是从尾部开始进行遍历、比较:
第二步对比过程
第三步是如果旧节点遍历完毕,但是依然有新的节点,那么就新增节点:
第四步是如果新的节点遍历完毕,但是依然有旧的节点,那么就移除旧节点:
第五步是最特色的情况,中间还有很多未知的或者乱序的节点:
所以我们可以发现,Vue在进行diff算法的时候,会尽量利用我们的key来进行优化操作:
- 在没有key的时候我们的效率是非常低效的;
- 在进行插入或者重置顺序的时候,保持相同的key可以让diff算法更加的高效;
1
1
1
1
1
1
1
1
1
1