背景:vue和react都是利用diff算法来进行对比,比较前后两次渲染的虚拟DOM树,找出它们之间的差异,生成一个补丁,并将这些差异应用到实际的DOM上,以更新用户界面
Vue中使用的虚拟DOM Diff算法可以简述如下:
1,Diff算法首先需要遍历两棵树的节点,将树的结构转换为一系列的操作指令。
2,进行同层比较,这个比较过程会根据节点的类型和key属性进行。如果新旧节点类型不相同,直接废弃老DOM
3,如果新旧节点的类型相同,则会比较它们的属性。如果有属性发生了变化,产生一个补丁包,用于更新对应的DOM属性。
4,如果节点的类型和key属性都相同,Diff算法会采用双端队列的方式递归比较它们的子节点,并且尽量复用。
同上
为什么不能写:key="index"
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
<ul class="list" id="list">
<li class="item" v-for="(item, index) in list" :key="index">{{item}}</li>
</ul>
<button @click="add">add</button>
</div>
<script>
const { createApp, ref } = Vue
createApp({
setup() {
const list = ref(['html', 'css', 'vue'])
const add = () => {
list.value.unshift('Ywis')
}
return {
list,
add
}
}
}).mount('#app')
</script>
</body>
</html>
在这个场景之中有一个 ul 列表和一个 按钮,每当我们按一下按钮时,就会向list数组开头添加一个新的元素('Ywis'), 这时就会发生compiler编译器重新编译、新旧虚拟DOM结构比较、浏览器重新渲染等一系列操作
这就是用index作为key的弊端所在了,索引是基于数组的顺序,而不是基于每个项的内容。如果列表中的项目发生了变化,例如向列表头部或者中间添加或删除了某个项目,这会导致相同的索引对应着不同的项,这样就会破坏Vue的虚拟DOM Diff算法,使得整个列表被浏览器重新渲染,性能大大的降低了
网友评论