vue2 数组的响应式处理

108 阅读2分钟

Object.defineProperty无法监听到数组的变化,所以vue通过拦截的方式来做数组的响应式

  1. 首先得到7个拦截的数组方法arrayMethods,除此之外arrayMethods也含有数组的其他方法。之所以是这七个,是因为这些方法都为增删改操作
const arrayProto = Array.prototype
// 创建一个新的对象来拓展数组的原型方法,在这些方法被调用时触发自定义的逻辑(响应式处理)
export const arrayMethods = Object.create(arrayProto)

// 定义要响应式拦截的数组方法,共七种
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
methodsToPatch.forEach(function (method) {
  // cache original method
  const original = arrayProto[method]
  // 通过Object.defineProperty的数据描述符的value,进行拦截
  def(arrayMethods, method, function mutator(...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    // push、unshift、splice这些方法可以新增,新增的数据也要转变为响应式
    switch (method) {
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    // 将新增的数据变为响应式
    if (inserted) ob.observeArray(inserted)
    // notify change
    if (__DEV__) {
      ob.dep.notify({
        type: TriggerOpTypes.ARRAY_MUTATION,
        target: this,
        key: method
      })
    } else {
      // 通知所有watcher视图更新
      ob.dep.notify()
    }
    return result
  })
})

  1. 判断处理的数据是数组时,改变它的__proto__指向为arrayMethods。并不会影响全局Array原型上的方法
class Observer {
  dep: Dep
  vmCount: number // number of vms that have this object as root $data

  constructor(public value: any, public shallow = false, public mock = false) {
    // this.value = value
    this.dep = mock ? mockDep : new Dep()
    this.vmCount = 0
    def(value, '__ob__', this)
    if (isArray(value)) {
      // 这里是数组的响应式处理
      if (!mock) {
        if (hasProto) {
          /* eslint-disable no-proto */
          // 改变当前数组对象的__proto__ 为拦截方法arrayMethods
          // 当使用上述七种方法时拦截
          ;(value as any).__proto__ = arrayMethods
          /* eslint-enable no-proto */
        } else {
          for (let i = 0, l = arrayKeys.length; i < l; i++) {
            const key = arrayKeys[i]
            def(value, key, arrayMethods[key])
          }
        }
      }
      if (!shallow) {
        this.observeArray(value)
      }
    } else {
      /**
       * Walk through all properties and convert them into
       * getter/setters. This method should only be called when
       * value type is Object.
       */
      // 这里是对象的响应式处理
      const keys = Object.keys(value)
      for (let i = 0; i < keys.length; i++) {
        const key = keys[i]
        defineReactive(value, key, NO_INITIAL_VALUE, undefined, shallow, mock)
      }
    }
  }
}