[Vue] Object.defineProperty 什么情况监听不到?和 Proxy 响应式原理又何区别?
前言
Vue 2.x
采用的是 Object.defineProperty
来实现响应式系统,它只能监听已经存在的属性,无法监听对象属性的新增或删除。
Vue 3
使用 Proxy
拦截对对象和数组的访问和修改,实现了响应式系统。它通过拦截这些操作,追踪哪些数据被访问、修改,从而在数据变化时通知相关的依赖。
Object.defineProperty
<template>
<div class="hello">
{{ user }}
<button @click="changeVal">Change name</button>
<button @click="addVal">Add new Val</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
data() {
return {
user: {
name: "Alice"
}
};
},
methods: {
changeVal() {
this.user.name = "Bob";
},
addVal() {
this.user.age = 25; // 视图不会更新
}
}
};
</script>
<style scoped></style>
通过 this.$set
新增属性:
this.$set(this.user, "age", 25);
Object.defineProperty
对数组的监听能力有限,push、pop、shift、unshift、splice、sort 和 reverse 数组操作 Vue 可以监听到,而其他的则不行,比如直接修改数组的某个索引值:
this.arrNums[1] = 10; // 不能监听,视图不会更新
Proxy
reactive
是通过 Proxy
实现的。通过 Proxy
来拦截对象的访问和修改操作,实现对数据的追踪和更新。
const handler = {
// 拦截属性的读取操作
get(target, key) {
console.log(`Getting property ${key}: ${target[key]}`);
return target[key];
},
// 拦截属性的设置操作(包括新增属性)
set(target, key, value) {
if (!target.hasOwnProperty(key)) {
console.log(`Adding new property ${key} with value ${value}`);
} else {
console.log(`Updating property ${key} to ${value}`);
}
target[key] = value;
return true; // 必须返回 true,表示成功
},
// 拦截属性的删除操作
deleteProperty(target, key) {
console.log(`Deleting property ${key}`);
delete target[key];
return true; // 必须返回 true,表示成功
}
};
// 创建一个代理对象
const person = new Proxy({}, handler);
// 新增属性
person.name = 'Alice'; // 控制台输出: Adding new property name with value Alice
person.age = 25; // 控制台输出: Adding new property age with value 25
// 修改现有属性
person.name = 'Bob'; // 控制台输出: Updating property name to Bob
// 读取属性
console.log(person.name); // 控制台输出: Getting property name: Bob
// 输出: Bob
// 删除属性
delete person.age; // 控制台输出: Deleting property age
当属性在对象中不存在时,通过 set 方法新增该属性。
当属性已经存在,再次通过 set 修改时,拦截器会识别为更新操作。