由于 defineReactive 所使用的 definePrototype 对数组无法进行有效的劫持,所以当 Vue 观测到数组后将会用另一种方式实现数据的监听。

  1. if (Array.isArray(value)) {
  2. // 判断当前环境是否有 __proto__,IE 11 以下就没有 __proto__
  3. if (hasProto) {
  4. // 正常情况下
  5. protoAugment(value, arrayMethods)
  6. } else {
  7. copyAugment(value, arrayMethods, arrayKeys)
  8. }
  9. // 遍历并深度检测
  10. this.observeArray(value)
  11. }
  12. // 覆盖原型链
  13. function protoAugment (target, src: Object) {
  14. target.__proto__ = src
  15. }
  16. // 通过代理覆盖原型上的函数
  17. function copyAugment (target: Object, src: Object, keys: Array<string>) {
  18. for (let i = 0, l = keys.length; i < l; i++) {
  19. const key = keys[i]
  20. def(target, key, src[key])
  21. }
  22. }

arrayMethods

一个进行加工过的数组原型,其中有关涉及修改数组内容的函数都已被挟持,触发后会通知依赖项更新,并如有新值加入则对新值进行深度监听。

  1. const arrayProto = Array.prototype // 数组原型
  2. export const arrayMethods = Object.create(arrayProto) // 数组原型浅拷贝,在此基础上修改,防止污染全局原型链
  3. // 涉及修改数组内容的函数 key 值
  4. const methodsToPatch = [
  5. 'push',
  6. 'pop',
  7. 'shift',
  8. 'unshift',
  9. 'splice',
  10. 'sort',
  11. 'reverse'
  12. ]
  13. // 遍历上面的 key 值
  14. methodsToPatch.forEach(function (method) {
  15. // 获取原先的函数
  16. const original = arrayProto[method]
  17. // 挟持这个会修改数组值的函数,分三步走
  18. // 1. 执行该函数的原函数
  19. // 2. 如果涉及添加新元素,则对新添加的元素进行深度检测
  20. // 3. 通知当前值的观察者进行更新
  21. def(arrayMethods, method, function mutator (...args) {
  22. const result = original.apply(this, args)
  23. const ob = this.__ob__
  24. let inserted
  25. switch (method) {
  26. case 'push':
  27. case 'unshift':
  28. inserted = args
  29. break
  30. case 'splice':
  31. inserted = args.slice(2)
  32. break
  33. }
  34. if (inserted) ob.observeArray(inserted)
  35. // notify change
  36. ob.dep.notify()
  37. return result
  38. })
  39. })