数组和对象类型当值变化时如何劫持到?

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);

前端茅台
14 声望0 粉丝

喜欢茅台的前端开发