数组和对象类型当值变化时如何劫持到?
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);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。