1、测试环境准备
<template>
<div>
<div ref="container">
<div v-for="item, index in dataList" :key="item.name">{{ item.name }}
<div style="margin-left: 10px;">{{ item.child }}</div>
</div>
</div>
<button @click="add">增加</button>
<button @click="insert">插入</button>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
const dataList = ref([{ name: 1, child: "1-1" }, { name: 2, child: "1-2" }]);
const add = () => {
const num = Math.random() * 100;
const child_num = `${num}_${Math.random() * 200}`;
dataList.value.push({ name: num, child: child_num });
}
const insert = () => {
const num = Math.random() * 100;
const child_num = `${num}_${Math.random() * 200}`;
dataList.value.splice(1, 0, { name: num, child: child_num });
}
const container = ref(null);
onMounted(() => {
// 创建一个 MutationObserver 实例,并指定回调函数
const observer = new MutationObserver((mutationRecords) => {
for (const record of mutationRecords) {
// 检查变动类型
if (record.type === 'childList') {
if (record.addedNodes.length > 0) {
for (const addedNode of record.addedNodes) {
console.log('新增的节点:', addedNode);
}
}
if (record.removedNodes.length > 0) {
for (const removedNode of record.removedNodes) {
console.log('移除的节点:', removedNode);
}
}
} else if (record.type === 'attributes') {
console.log('属性变化:', record.target, '属性名称:', record.attributeName);
} else if (record.type === 'characterData') {
console.log('文本内容变化:', record.target);
}
}
});
// 开始观察指定的 DOM 节点及其子节点的变动
observer.observe(container.value, {
childList: true, // 监听子节点的添加和删除
attributes: true, // 监听属性变化
characterData: true, // 监听文本内容变化
subtree: true // 监听整个子树
});
});
</script>
<style lang="scss" scoped></style>
mutationObserver:提供了监视对 DOM 树所做更改的能力
有了这个测试环境,所有对dom的更改我们都可以一目了然
2、key为唯一值的情况
当key为一个唯一值的情况下,这也是vue推荐的做法:


我们可以看到仅新增了5.812019637998778一个节点,其他节点并没有移动、删除的操作,这是因为vue会根据key进行最大可能的dom复用。
3、key没有设置的情况下


可以看出,vue对元素进行了“就地复用”,也就是仅仅按照顺序将内容进行了覆盖,原来“2”这个文本被覆盖为了90_141,“1-2”子节点会被删除,然后新增“90_141”子节点,然后重新创建了2号节点和1-2子节点,这对于长列表和深层结构的组件显然是会浪费巨量的渲染时间,所以我们应该尽量设置唯一的key值辅助vue进行渲染操作。