前置知识
vue2 的响应式
通过 Object.defineProperty 对对象的已有属性值的读取和修改方法进行重写, 此外Object.defineProperty 不能对数组的属性的读写进行拦截, 数组需要通过重写数组的一些方法来实现对元素操作的。
用 Object.defineProperty 实现对对象中所有属性的读写访问进行拦截
// 定义一个对象
let user = {
name: "mike",
age: 12
}
// 遍历所有对象中的 key 进行属性的拦截,因为需要遍历所有属性,所以会有性能上的影响。
for (let key in user) {
let value = user[key]
Object.defineProperty(user, key, {
get() {
console.log( "value = " + value);
return value;
},
set(newValue){
if (value !== newValue) {
// 监听新赋值的
// TODO
this.observer(newValue);
console.log( value + "(old)"+ " ==> "+ newValue + "(new)");
value = newValue;
}
}
})
}
user.name = { name: 'abc' };
通过重写数组 Array 的方法,实现对数组读取的拦截
例如重写数组中的 push,pop,shift,unshift 方法:
// 拷贝一份 Array 的原型方法
let proto = Object.create(Array.prototype);
// 重写数组中的 push,pop,shift,unshift 方法
['push','pop','shift','unshift'].forEach(methodName=>{
// 重写 methodName 指向的方法,最终还是需要调用 Array 原型方法,
// 相当于拦截该方法的执行,在拦截中实现自己的操作
proto[methodName]=function(){
// TODO
console.log("执行数组中"+ methodName +"方法")
Array.prototype[methodName].call(this,...arguments)
}
})
let arr = [1,2,3,4,5,6,7,8]
arr.__proto__ = proto
arr.push(9) // 执行数组中push方法 1,2,3,4,5,6,7,8,9]
vue3 的响应式
Proxy 对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
用 Proxy 代理对象
let user = {
name: "he.wenyao",
age: 18
}
// 创建一个代理对象 user_proxy 是对 user 的一个代理,
// 对 user_proxy 所作的操作也会同步到 user 上
let user_proxy = new Proxy(user, {
set: function (target, key, value, receiver) {
// TODO 你想在赋值操作的时候做点什么?
console.log(`赋值操作${String(key)} = ${value}`);
return Reflect.set(target, key, value, receiver);
},
get: function (target, key, receiver) {
// TODO 你想在获取值的时候做点什么?
return Reflect.get(target, key, receiver);
}
});
user_proxy['age'] = 1; // 赋值操作age = 1 \n 1
用 Proxy 代理数组
let arr = [1, 2, 3]
let proxy = new Proxy(arr, {
get: function (target, key, receiver) {
console.log('get的key为 ===>' + key);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
console.log('set的key为 ===>' + key, value);
return Reflect.set(target, key, value, receiver);
}
})
proxy[0] // set的key为 ===>0
proxy[3] = 12 // set的key为 ===>3 12
Vue2 和 Vue3 代理的区别
- Vue2 中使用 Object.defineProperty 在重写对象的读写访问时,只能一个个属性进行设置,需要遍历对象中的所有属性,才能完成对一个对象的所有属性的读写拦截,影响性能,而且不能对数组的属性读写进行拦截,数组属性读写的拦截只能通过复写一个个数组属性方法。
- Vue2 中使用 Proxy 对对象和数组的所有属性的读写进行拦截,不用一个个属性进行拦截。