数组和对象类型当值变动时如何劫持到?
a、对象通过 Object.defineProperty 将属性进行劫持;多重对象通过递归进行实现。
export function defineReactive (
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
// 1. 如果对象不可配置则间接退出
const property = Object.getOwnPropertyDescriptor(obj, key)
if (property && property.configurable === false) {return}
// 2. 获取 getter 和 setter
const getter = property && property.get
const setter = property && property.set
// 3. 从新定义 set 和 get 办法
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter () {const value = getter ? getter.call(obj) : val
return value
},
set: function reactiveSetter (newVal) {const value = getter ? getter.call(obj) : val
if (newVal === value || (newVal !== newVal && value !== value)) {return}
if (getter && !setter) return
if (setter) {setter.call(obj, newVal)
} else {val = newVal}
}
})
}
这种形式只会监听对象自带的属性,新增的属性监听不到,因而对于新增的属性应用 $set 形式进行数据新增,$set 办法外部会将新增属性定义成响应式数据
b、数组通过重写办法
export const arrayMethods = Object.create(arrayProto)
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
methodsToPatch.forEach(function (method) {
// cache original method
const original = arrayProto[method]
def(arrayMethods, method, function mutator (...args) {const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
// 对新增的属性再次进行观测
if (inserted) ob.observeArray(inserted)
return result
})
})
当操作数组时会调用重写的数组办法,这时就能够监测到数据的变动。因而,如果批改数组索引和长度是不会监听到数组的变动的。
Vue3 中应用 ES6 的 Proxy 代理,Proxy 能够拦挡指标对象的底层操作
let obj = {name: {name: 'lh'}
}
let handler = {get(target,key){ // 这里的命名必须和 Reflect 办法对应
if(typeof target[key] === 'object' && target[key] !== null){return new Proxy(target[key],handler);
}
return Reflect.get(target,key);
},
set(target,key,value){let oldValue = target[key];
if(!oldValue){console.log('新增属性')
}else if(oldValue !== value){console.log('批改属性')
}
return Reflect.set(target,key,value);
}
}
let proxy = new Proxy(obj,handler);