转载来源: https://www.cnblogs.com/lyt0207/p/12540091.html
消除了之前 Vue2.x
中基于 Object.defineProperty
的实现所存在的很多限制:无法监听 属性的添加和删除 、数组索引和长度的变更 ,并可以支持 Map
、Set
、WeakMap
和 WeakSet
!
什么是 Proxy?
MDN 上是这么描述的——Proxy
对象用于定义基本操作的自定义行为(如属性查找,赋值,枚举,函数调用等)。
其实就是在对目标对象的操作之前提供了拦截,可以对外界的操作进行过滤和改写,修改某些操作的默认行为,这样我们可以不直接操作对象本身,而是通过操作对象的代理对象来间接来操作对象,达到预期的目的~
看一个例子:
let obj = {
name:{ name:'hhh' } ,
arr: [ '吃' ,'喝' ,'玩' ]
}
//proxy兼容性差 可以代理13种方法 get set
//defineProperty 只对特定 的属性进行拦截
let handler = {
get ( target,key) { //target就是obj key就是要取obj里面的哪个属性
console.log( '收集依赖' )
return target[ key]
} ,
set ( target,key,value) {
console.log( '触发更新' )
target[ key] = value
}
}
let proxy = new Proxy( obj,handler)
//通过代理后的对象取值和设置值
proxy.arr
proxy.name = '123'
定义了一个对象obj,通过代理后的对象(上面的proxy)来操作原对象。当取值的时候会走get方法,返回对应的值,当设置值的时候会走set方法,触发更新。但这是老的写法,新的写法是使用Reflect。
Reflect是内置对象,为操作对象而提供的新API,将Object对象的属于语言内部的方法放到Reflect对象上,即从Reflect对象上拿Object对象内部方法。 如果出错将返回false
简单改写上面这个例子
let handler = {
get ( target,key) { //target就是obj key就是要取obj里面的哪个属性
console.log( '收集依赖' )
// return target[ key]
//Reflect 反射 这个方法里面包含了很多api
return Reflect.get( target,key)
} ,
set ( target,key,value) {
console.log( '触发更新' )
// target[ key] = value //这种写法设置时如果不成功也不会报错 比如这个对象默认不可配置
Reflect.set( target,key,value)
}
}
let proxy = new Proxy( obj,handler)
//通过代理后的对象取值和设置值
proxy.arr
proxy.name = '123'
但是有一个问题,这个对象是多层对象,之前Object.defineProperty方法是一开始就会对这个多层对象进行递归处理,而Proxy不会。它是懒代理。如果对这个对象里面的值进行代理就取不到值。就像上面我们只对name进行了代理,并没有对name.name进行代理,所以他就取不到这个值,需要代理之后才能取到。
let obj = {
name:{ name:'hhh' } ,
arr: [ '吃' ,'喝' ,'玩' ]
}
//proxy兼容性差 可以代理13种方法 get set
//defineProperty 只对特定 的属性进行拦截
let handler = {
get ( target,key) { //target就是obj key就是要取obj里面的哪个属性
console.log( '收集依赖' )
if( typeof target[ key] == = 'object' && target[ key] != = null) {
//递归代理,只有取到对应值的时候才会代理
return new Proxy( target[ key] ,handler)
}
// return target[ key]
//Reflect 反射 这个方法里面包含了很多api
return Reflect.get( target,key)
} ,
set ( target,key,value) {
console.log( '触发更新' )
// target[ key] = value //这种写法设置时如果不成功也不会报错 比如这个对象默认不可配置
Reflect.set( target,key,value)
}
}
let proxy = new Proxy( obj,handler)
可以看到这次虽然我写了代理name.name,但是没有取到name.name,它并不会真正的代理
这次触发了两次收集依赖。
接下来看看数组的代理过程:
let obj = {
name:{ name:'hhh' } ,
arr: [ '吃' ,'喝' ,'玩' ]
}
//proxy兼容性差 可以代理13种方法 get set
//defineProperty 只对特定 的属性进行拦截
let handler = {
get ( target,key) { //target就是obj key就是要取obj里面的哪个属性
console.log( '收集依赖' )
if( typeof target[ key] == = 'object' && target[ key] != = null) {
//递归代理,只有取到对应值的时候才会代理
return new Proxy( target[ key] ,handler)
}
// return target[ key]
//Reflect 反射 这个方法里面包含了很多api
return Reflect.get( target,key)
} ,
set ( target,key,value) {
console.log( '触发更新' )
// target[ key] = value //这种写法设置时如果不成功也不会报错 比如这个对象默认不可配置
return Reflect.set( target,key,value)
}
}
let proxy = new Proxy( obj,handler)
//通过代理后的对象取值和设置值
// proxy.name.name = '123' //设置值,取一次,设置一次
proxy.arr.push( 456)
这里面它会走两次触发更新的操作,因为第一次需要修改数组的长度,第二次再把元素放进数组里。所以我们需要判断一下它是新增操作还是修改操作
set ( target,key,value) {
let oldValue = target[ key]
console.log( key, oldValue, value)
if( ! oldValue) {
console.log( '新增属性' )
} else if( oldValue != = value) {
console.log( '修改属性' )
}
// target[ key] = value //这种写法设置时如果不成功也不会报错 比如这个对象默认不可配置
return Reflect.set( target,key,value)
}
首先拿到它的旧值,如果这个值不存在就是新增,如果存在但不相等就是修改操作
可以看到最后一次判断结果是两个相等,什么也不做
这是一次修改操作。
我们知道,vue2.0是不会监控到之前不存在的属性的,但是proxy可以操作之前不存在的属性的,它会拦截设置操作,如下:
xxx之前并不在obj对象里面,但是依旧可以新增。
转载来源: https://www.cnblogs.com/lyt0207/p/12540091.html