问题重现
在以下案例中,使用 watch 来深度监控整个 person 对象时存在 person.name 改变前和改变后的值都是一样的问题。这是因为 Vue 的响应性系统无法检测对象内部属性的变化,只能检测对象引用的变化,因此直接监听整个 person 对象时可能无法正确触发。
<template>
<div>
<el-button @click="changeName">改变</el-button>
</div>
</template>
<script>
export default {
data() {
return {
person: {
name: "oldName",
},
};
},
watch: {
person: {
handler(newValue, oldValue) {
console.log(oldValue.name + "-->" + newValue.name);
// 输出: newName-->newName
},
deep: true,
},
},
methods: {
changeName() {
this.person.name = "newName";
},
},
};
</script>
解决方案
(1)序列化与反序列化:代码行数少,但可能导致性能问题和丢失原型链。在处理特殊值(如 undefined 或 NaN )时存在问题,通常情况下不太推荐使用。
(2)深拷贝:在大型项目中执行效率更高,尤其是对比 JSON.parse(),更具优势。
<template>
<div>
<el-button @click="changeName">改变</el-button>
</div>
</template>
<script>
export default {
data() {
return {
person: {
name: "oldName",
},
};
},
watch: {
// case 1: 使用序列化与反序列化
newPerson: {
handler(newValue, oldValue) {
console.log("newPerson:" + oldValue.name + "-->" + newValue.name);
// 输出: newPerson:oldName-->newName
},
deep: true,
},
// case 2: 深拷贝
newPerson2: {
handler(newValue, oldValue) {
console.log("newPerson2:" + oldValue.name + "-->" + newValue.name);
// 输出: newPerson2:oldName-->newName
},
deep: true,
},
},
methods: {
changeName() {
this.person.name = "newName";
},
},
computed: {
// 使用计算属性进行深拷贝
newPerson() {
return JSON.parse(JSON.stringify(this.person));
},
newPerson2() {
return this.$lodash.cloneDeep(this.person);
},
},
};
</script>