开始vue3响应式Proxy之前,先回顾一下vue2的响应式原理
vue2响应式
- 实现原理:
- 对象类型:通过
Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)。 - 数组类型:通过重写更新数组的一系列方法来实现拦截。(对数组的变更方法进行了包裹)。
- 对象类型:通过
Object.defineProperty(data, 'count', {
get () {}, // 读取
set () {} // 设置
})
- 模拟vue2实现响应式
const person = {
name:'张三',
age:18
}
let p = {}
Object.keys(person).forEach(key=>{
Object.defineProperty(p,key,{
configurable:true,
get(){ // 读取时触发
return person[key]
},
set(value){ //修改时触发
console.log('有人修改了person,我发现了,我要去更新界面!');
return person[key] = value
}
})
})
新增时不会触发set()方法,虽然添加了,但是不是响应式数据,从图片中可以看出 age和name是拥有get()set()方法的,拥有get()set()方法才是响应式数据
删除属性时从图片中可以看出没有删除成功,delete
删除属性时会返回一个Boolean告诉我们是否删除成功,在Object.defineProperties
中有一个属性configurable
设置为true,就可以删除了
- 存在问题:
- 新增属性、删除属性, 界面不会更新。
- 直接通过下标修改数组, 界面不会自动更新。
- 解决方法:
- this.$set(源数据,属性,值)
- this.$delete(源数据,属性)
- Array.splice()
vue3响应式Proxy
- 实现原理:
- 通过Proxy(代理): 拦截对象中任意属性的变化, 包括:属性值的读写、属性的添加、属性的删除等。
- 通过Reflect(反射): 对源对象的属性进行操作。
-
-
MDN文档中描述的Proxy与Reflect:
-
new Proxy(data, {
// 拦截读取属性值
get (target, prop) {
return Reflect.get(target, prop)
},
// 拦截设置属性值或添加新属性
set (target, prop, value) {
return Reflect.set(target, prop, value)
},
// 拦截删除属性
deleteProperty (target, prop) {
return Reflect.deleteProperty(target, prop)
}
})
- Reflect(反射) 通过Reflect读取对象属性 Reflect.get(obj,xxx) 修改对象属性 Reflect.set(obj,'xxx',xxx) 删除对象属性 Reflect.deleteProperty(obj,xxx)
Object.defineProperty(obj,'c',{
get(){
return 3
}
})
Object.defineProperty(obj,'c',{
get(){
return 4
}
})
// 直接报错 cannot redefine property:c 不往下执行
const r1 = Reflect.defineProperty(obj,'c',{
get(){
return 3
}
})
const r1 = Reflect.defineProperty(obj,'c',{
get(){
return 4
}
})
console.log('继续执行了',r1,r2); //r1-->true r2 -->false
Reflect 在框架封装中可以减少try catch的使用 更友好一点
- 模拟vue3响应式
const person = {
name:'张三',
age:18
}
// Proxy 第一个参数是源数据,第一个参数为必填,可以为空对象
let p = Proxy(person,{}) // 这样可以实现一个代理
const person = {
name:'张三',
age:18
}
// Proxy 第一个参数是源数据,第一个参数为必填,可以为空对象 为空对象时可以实现代理,但是代理不是响应式
// let p = Proxy(person,{})
let p = Proxy(person,{
// get 有两个参数 target和propName target-->源数据 propName-->读取的属性名
get(target,propName){
console.log(`有人读取了p身上的${propName}属性`);
return Reflect.get(target, propName)
},
// set 有三个参数 target和propName target-->源数据 propName-->读取的属性名 value-->修改的值
// proxy的set方法可以监听到修改和新增
set(target,propName,value){
console.log(`有人修改了p身上的${propName}属性,我要去更新页面了!`);
return Reflect.set(target, propName, value)
},
// deleteProperty 有两个参数 target和propName target-->源数据 propName-->读取的属性名
deleteProperty(target,propName){
console.log(`有人删除了p身上的${propName}属性,我要去更新页面了!`);
return Reflect.deleteProperty(target, propName)
}
})
完结散花...