vue2 的响应式
使用 Object
构造函数上 defineProperty()
实现
存在的问题 :
-
对象
新增的属性没有响应式
-
数组
部分操作没有响应式
解决办法
1、 用 Vue.set
进行添加或修改,Vue.delete
进行删除。
2、使用数组的一些方法对数组操作 (如 push()
,splice()
,pop()
,shift()
,unshift()
,sort()
,reverse()
3、使用 vue
实例对象上的 $set
进行添加或修改, $delete
进行删除。
4、使用 vue
实例对象上的 $nextTick
进行页面更新
实现响应式的原理
-
对象类型:
通过 Object.defineProperty()
对属性的读取,修改进行拦截(数据劫持)
-
数组类型:
通过重写更新数组的方法来实现拦截(对数组的变更方法进行包裹)
Object.defineProperty参数说明
Object.defineProperty("对象", "属性", {
value: 0, // 属性值
enumerable: true, // 属性是否可被枚举,默认 false
writable: true, // 属性是否可被修改,默认 false
configurable: true, // 属性是否可被删除,默认 false
get() {}, // 获取属性值时调用,此函数需返回属性的属性值
set(value) {}, // 修改属性值时调用,value为修改后的值
})
vue2 实现响应式
// 定义源对象
let person = {
name:"张三",
age:18
}
// 定义代理对象
let obj = {}
Object.defineProperty(obj ,"name",{
get(){
return person.name
},
set(val){
console.log(val)
person.name = val +"aaa"
}
})
console.log(obj .name);
obj.name = "李四"
// 更改obj.name的值,会触发set方法
console.log(person) // name = "李四aaa" 因为set方法return时是val+"aaa"
vue3 的响应式
使用 window
上的内置构造函数 Proxy
实现。
实现原理
-
Proxy(代理):
拦截对象中任意属性的变化,包括属性值的读取、修改,以及属性的添加和删除
-
Reflect(反射):
对源对象的数学进行操作。
// 定义源对象
let person = {
name:"zhangsan",
age:18
}
// 定义代理对象
let p = new Proxy(person,{
// target:源对象,key:源对象的属性名,val:源对象的属性值,receiver:代理对象,一般不用,可以不写,默认是代理对象,也就是p,如果写了,就是写的值,比如写了p1,那么就是p1,如果不写,就是p
get(target,key){
return target[key]
},
// val:更改后的值,也就是p.name = "lisi"中的lisi,也就是set方法中的val,也就是p.name = val中的val
set(target,key,val){
console.log(val)
target[key] = val +"aaa"
},
deleteProperty(target,key){
console.log(target,key)
return delete target[key]
}
})
console.log(p);
但是上述代码依然存在问题。例如:
const p1 = Object.defineProperty(person,"name",{
get(){
return "李四"
}
})
// 在这里会出现异常
const p2 = Object.defineProperty(person,"name",{
get(){
return "王五"
}
})
console.log(person)
在上述代码中,因为我们重复的对 person
的name
属性进行了操作,就会出现异常,导致单线程挂掉,如果需要解决出现的问题,需要用try...catch
对异常进行捕获
同时,我们也可以用 Reflect
处理这种问题
Relect
是 window
上的内置构造函数,与 Object
不同的是,Reflect
会返回一个布尔值,可以更灵活地捕捉到异常,判断操作是否成功,从而进一步对错误进行抛出
实现响应式可以优化为:
let p = new Proxy(person,{
get(target,key){
return Reflect.get(target,key)
},
set(target,key,val){
return Reflect.set(target,key,val)
},
deleteProperty(target,key){
return Reflect.deleteProperty(target,key)
}
})