Vue 3 的 Diff 算法会受到 v-for
循环中的 key
属性的影响 ,key
的选择直接关系到 Diff 算法的效率和最终的 DOM 更新结果。
key
的作用
在 Vue 中,key
是一种标识,它用于唯一标记每个虚拟 DOM 节点。Diff 算法会根据 key
判断新旧节点是否是同一个节点。
没有 key
时
- 如果没有
key
,Vue 会默认使用节点的索引值作为标识。 - 当列表发生变化时,由于索引可能对应了错误的节点,会导致无法正确复用现有节点,从而增加不必要的 DOM 操作。
有 key
时
- 当指定了唯一的
key
,Vue 可以准确匹配新旧节点。 - 这样可以复用相同
key
的节点,避免误匹配或冗余操作,提高性能。
Diff 算法如何处理 key
-
key
匹配规则- 在 Diff 过程中,如果新旧节点的
key
相同,Vue 会认为它们是相同的节点,执行更新操作而非重建。 - 如果
key
不同,Vue 会移除旧节点并创建新节点。
- 在 Diff 过程中,如果新旧节点的
-
有
key
的场景- Vue 通过
key
快速定位新旧节点在列表中的位置,避免盲目比较,从而优化性能。 - Vue 3 使用 最长递增子序列(LIS) 算法来减少 DOM 操作,
key
的稳定性是实现该优化的重要前提。
- Vue 通过
-
没有
key
的场景- Vue 会退化为按索引逐一比较的模式。
- 如果数据顺序发生变化,可能会导致大量的节点重建和移动。
案例分析
示例 1:没有 key
的情况
vue
<template>
<div>
<div v-for="item in items">{{ item }}</div>
</div>
</template>
<script>
export default {
data() {
return {
items: [1, 2, 3]
};
},
mounted() {
setTimeout(() => {
this.items = [3, 2, 1]; // 改变顺序
}, 1000);
}
};
</script>
结果:
- Vue 会将
[1, 2, 3]
和[3, 2, 1]
按索引逐个对比。 - 由于没有
key
,节点内容不同,导致所有节点被替换,性能较差。
示例 2:使用唯一的 key
vue
<template>
<div>
<div v-for="item in items" :key="item">{{ item }}</div>
</div>
</template>
<script>
export default {
data() {
return {
items: [1, 2, 3]
};
},
mounted() {
setTimeout(() => {
this.items = [3, 2, 1]; // 改变顺序
}, 1000);
}
};
</script>
结果:
- Vue 根据
key
匹配节点[1, 2, 3]
和[3, 2, 1]
,只移动节点位置,而不会销毁重建。 - 这样可以显著减少 DOM 操作,提高性能。
key
影响 Diff 的性能与结果
-
性能影响
- 没有
key
:Vue 需要逐个比对每个节点的内容,无法充分复用节点。 - 有
key
:Vue 可以快速确定哪些节点需要更新、移动或删除,从而减少不必要的 DOM 操作。
- 没有
-
更新结果的准确性
- 没有
key
:可能会导致节点错误复用,渲染结果不符合预期。 - 有
key
:可以确保每个节点的复用和更新都是正确的。
- 没有
html
<template>
<div class="ForKeyInDiffVNode">
<el-button @click="change">改变值</el-button>
<div class="item-class" v-for="(item, idx) in lists" :key="idx">
<el-checkbox></el-checkbox>
<span>{{ item.name }}</span>
</div>
</div>
</template>
<script setup lang="ts">
import {ref} from "vue";
const lists = ref([
{
name: 'hmk',
age: 20,
show: false
},
{
name: '张三',
age: 21,
show: false
},
{
name: '李四',
age: 22,
show: false
},
{
name: '王五',
age: 23,
show: false
}
])
// 之前选中第三个,点击删除后,会出现错乱情况
// 给key加上一个唯一的id即可解决问题
const change = () => {
lists.value.splice(1, 1)
}
</script>
最佳实践:key
的选择
-
使用唯一标识符
-
使用数据中的唯一标识(如 ID)作为
key
:vue<div v-for="item in items" :key="item.id">{{ item.name }}</div>
-
避免使用非唯一的值(如索引)作为
key
,因为数据顺序变化时可能会导致错误复用。
-
-
避免使用索引作为
key
-
索引是动态的,当列表顺序改变或元素插入时,索引会变化,导致 Diff 结果不准确:
vue<div v-for="item in items" :key="index">{{ item }}</div>
-
如果数据内容变化频繁,索引
key
会导致不必要的 DOM 重建。
-
-
保证
key
的稳定性key
应该在组件的整个生命周期内保持不变,否则会导致 Diff 结果错误。
总结
-
key
对 Diff 算法至关重要:- 没有
key
:Vue 会按索引匹配,导致错误复用和性能下降。 - 有
key
:Vue 可高效、准确地更新节点,减少 DOM 操作。
- 没有
-
选择唯一且稳定的
key
:- 推荐使用数据中的唯一标识符(如
id
)。 - 避免使用索引或动态值作为
key
。
- 推荐使用数据中的唯一标识符(如
-
Diff 性能优化:
- Vue 3 使用
key
时可以充分利用最长递增子序列(LIS)优化算法,最小化 DOM 移动和更新操作,提高性能。
- Vue 3 使用