DEV Community

JacobHsu
JacobHsu

Posted on

Vue Core

vue對數組的observe做了哪些處理?

Vue的Observer對數組做了單獨的處理,對數組的方法進行編譯,並賦值給數組屬性的proto屬性上,因為原型鏈的機制,找到對應的方法就不會繼續往上找了。編譯方法中會對一些會增加索引的方法(push,unshift,splice)進行手動observe。

vue的Observer類定義在core/observer/index.js

export class Observer {
   if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      this.observeArray(value)
    } else {
      this.walk(value)
    }

可以看到,vue的Observer對數組做了單獨的處理。

src/core/util/env.js

// can we use __proto__?
export const hasProto = '__proto__' in {}

hasProto 是判斷數組的實例是否有__proto__屬性,如果有proto 屬性就會執行protoAugment 方法,將arrayMethods 重寫到原型上。

src\core\observer\index.js

/**
 * Augment a target Object or Array by intercepting
 * the prototype chain using __proto__
 */
function protoAugment (target, src: Object) {
  /* eslint-disable no-proto */
  target.__proto__ = src
  /* eslint-enable no-proto */
}

arrayMethods 是對數組的方法進行重寫,定義在core/observer/array.js

// 复制数组构造函数的原型,Array.prototype也是一个数组。
const arrayProto = Array.prototype
// 创建对象,对象的__proto__指向arrayProto,所以arrayMethods的__proto__包含数组的所有方法。
export const arrayMethods = Object.create(arrayProto)

// 下面的数组是要进行重写的方法
const methodsToPatch = [
  'push',
  'pop',
  'shift',
  'unshift',
  'splice',
  'sort',
  'reverse'
]

/**
 * Intercept mutating methods and emit events
 */
// 遍历methodsToPatch数组,对其中的方法进行重写
methodsToPatch.forEach(function (method) {
  // cache original method
  const original = arrayProto[method]

  // def方法定义在lang.js文件中,是通过object.defineProperty对属性进行重新定义。
  // 在arrayMethods中找到我们要重写的方法,对其进行重新定义
  def(arrayMethods, method, function mutator (...args) {
    const result = original.apply(this, args)
    const ob = this.__ob__
    let inserted
    switch (method) {
      // 对于push,unshift会新增索引,所以需要手动observe
      case 'push':
      case 'unshift':
        inserted = args
        break
      case 'splice':
        inserted = args.slice(2)
        break
    }
    if (inserted) ob.observeArray(inserted)
    // notify change
    ob.dep.notify()
    return result
  })
})

src\core\util\lang.js

/**
 * Define a property.
 */
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
  Object.defineProperty(obj, key, {
    value: val,
    enumerable: !!enumerable,
    writable: true,
    configurable: true
  })
}

references:
【深入vue】為什麼Vue3.0不再使用defineProperty實現數據監聽?

Top comments (0)