由于 defineReactive 所使用的 definePrototype 对数组无法进行有效的劫持,所以当 Vue 观测到数组后将会用另一种方式实现数据的监听。
if (Array.isArray(value)) {// 判断当前环境是否有 __proto__,IE 11 以下就没有 __proto__if (hasProto) {// 正常情况下protoAugment(value, arrayMethods)} else {copyAugment(value, arrayMethods, arrayKeys)}// 遍历并深度检测this.observeArray(value)}// 覆盖原型链function protoAugment (target, src: Object) {target.__proto__ = src}// 通过代理覆盖原型上的函数function copyAugment (target: Object, src: Object, keys: Array<string>) {for (let i = 0, l = keys.length; i < l; i++) {const key = keys[i]def(target, key, src[key])}}
arrayMethods
一个进行加工过的数组原型,其中有关涉及修改数组内容的函数都已被挟持,触发后会通知依赖项更新,并如有新值加入则对新值进行深度监听。
const arrayProto = Array.prototype // 数组原型export const arrayMethods = Object.create(arrayProto) // 数组原型浅拷贝,在此基础上修改,防止污染全局原型链// 涉及修改数组内容的函数 key 值const methodsToPatch = ['push','pop','shift','unshift','splice','sort','reverse']// 遍历上面的 key 值methodsToPatch.forEach(function (method) {// 获取原先的函数const original = arrayProto[method]// 挟持这个会修改数组值的函数,分三步走// 1. 执行该函数的原函数// 2. 如果涉及添加新元素,则对新添加的元素进行深度检测// 3. 通知当前值的观察者进行更新def(arrayMethods, method, function mutator (...args) {const result = original.apply(this, args)const ob = this.__ob__let insertedswitch (method) {case 'push':case 'unshift':inserted = argsbreakcase 'splice':inserted = args.slice(2)break}if (inserted) ob.observeArray(inserted)// notify changeob.dep.notify()return result})})
