Vue 源码之数组更新检测
Vue 通用 Object.defineProperty 劫持数组,发布订阅模式添加订阅或者触发通知,从而实现双向绑定的。
本篇主要讲讲对于数组的变化, Vue 是如何检测的。
变异数组方法
Vue 创建了变异数组方法, 即保持数组方法原有功能不变的前提下对其进行功能拓展。
例子:
在原有函数输出的基础上,再输出 test
function fn(a){ console.log(a) }
var cache = fn;
fn = function(a){
console.log('test')
cache (a)
}
fn(123)
//输出:
//test
//123
数组更新检测
Vue 对 push()、pop()、shift()、unshift()、splice()、sort()、reverse() 等七个方法进行了功能拓展(变异数组)。
创建一个新对象继承原数组函数的方法,代理新对象的数组方法,返回和原数组一样的值,同时为数组列表注册订阅者对象,并发送通知更新响应属性
位置:./src/core/observer/array.js
源码:
const arrayProto = Array.prototype // 储存数组原型方法
export const arrayMethods = Object.create(arrayProto) // 创建一个新对象继承数组
;[ 'push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse' ]
.forEach(function (method) {
const original = arrayProto[method] // 缓存原数组方法
/*
* def 函数:重新定义这个新数组对象
* arrayMethods[method] 的值为 mutator 的返回值
*/
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args) // 运行的结果仍为原数组方法运行结果,并返回这个值
const ob = this.__ob__ // __ob__ 监听属性
let inserted // 处理数组方法的入参
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break
}
if (inserted) ob.observeArray(inserted) // observeArray:观察数组项的列表(为每一项添加__ob__属性,即监听器)
ob.dep.notify() // 给订阅者发送消息,触发更新
return result
})
})
def 函数是通过 Object.defineProperty 重新定义了对象的值,貌似Vue 3.x 以后准备使用 Proxy 来进行这种代理行为
源码:
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}
observeArray 就是简单的遍历一遍数组,然后给数组添加监听器
源码:
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}