Watcher 自动更新视图: push()pop()shift()unshift()splice()sort()reverse()工程源码:
src/core/observer/array.js
// 获取数组的原型 Array.prototype, 为了方便拿到数组原有的方法
const arrayProto = Array.prototype// 创建一个空对象 arrayMethods, 并将 arrayMethods 的原型指向 Array.prototype
export const arrayMethods = Object.create(arrayProto)// 列出需要重写的方法
const methodsToPatch = ['push','pop','shift','unshift','splice','sort','reverse'
]// 遍历列出的方法
methodsToPatch.forEach(function (method) {// 找到原来的函数体const original = arrayProto[method]// def 就是 defineProperty() 可以参见 util/lang.jsdef(arrayMethods, method, function mutator (...args) {// 调用原来的函数const result = original.apply(this, args)// 该数组是响应式的时候, 上面会有一个 __ob__ 属性const ob = this.__ob__let insertedswitch (method) {case 'push':case 'unshift':inserted = argsbreakcase 'splice':inserted = args.slice(2)break}// 判断如果添加的元素是对象或数组, 这些处理不重要if (inserted) ob.observeArray(inserted)// 重点在这里: 每个响应式数组上都会有一个 __ob__ 利用我们保留的 __ob__ 属性获取 notify 方法来通知 Watcher 更新视图ob.dep.notify()return result})
})

**Object.create()**方法用于创建一个对象,使现有的对象作为新创建对象的原型(prototype)
notify() 通知 Watcher 更新视图, 最后将原型也指向 Array 的原型, 形成了一种继承关系setData()setState()网友对尤雨溪提出的 issues:
https://github.com/vuejs/vue/issues/8562
Object.defineProperty() 方法// 对象
const obj = {age: 18 // 键为 age 值为 18
}// 数组
const arr = [18] // 等同于键为 0, 值为 18
监测数组
通过结果可以观察出, 如果数据量提升到十万甚至百万, 对数组进行修改时, 性能损耗会非常大
Vue 官方通过权衡后, 觉得这样做性价比不高, 所以没有对数组进行监测
尤雨溪原话
性能代价和获得的用户体验收益不成正比。
const arr = []defineReactive(arr, 0, 'a')
defineReactive(arr, 1, 'b')
defineReactive(arr, 2, 'c')
defineReactive(arr, 3, 'd')
defineReactive(arr, 4, 'e')console.log(arr) // 访问数组的所有方法, 所有属性的 get 都会执行一次
arr.push('f') // 末尾追加, 所以只需要把数组中所有元素遍历一遍, 所有属性 get 都会执行一次
arr.unshift('-a') // 开头插入, 会导致数组元素顺序发生变化, 所有属性的 get 和 set 都会执行一次
工程源码:
src\core\observer\index.js
复习 $set 使用方法
export function set (target, key, val) {// 判断是否为数组if (Array.isArray(target)) {// 这里的操作是怕传入一个越界的索引, 往里面添加数据// 传入的索引和当前长度取最大值target.length = Math.max(target.length, key)// 删除原来位置的元素, 然后在当前位置添加一个数据, 就完成了替换更新// 而由于 splice() 是数组重写的方法, 所以会重新更新页面target.splice(key, 1, val)return val}// 如果是对象就走后面的代码, 如果是数组就不看后面的了if (key in target && !(key in Object.prototype)) {// 如果你修改的对象, 属性在原来就存在, 说明已经被劫持了// 直接修改对象属性即可, 会自动更新视图target[key] = valreturn val}const ob = target.__ob__if (!ob) {target[key] = valreturn val}defineReactive(ob.value, key, val)ob.dep.notify()return val
}
$set 方法, 内部做了判断, 分为以下三种情况: splice()defineReactive() 进行数据劫持