vue中如何监测数组的变化
一、数组监听与对象监听的不同对象的监听是直接递归使用Object.defineProperty重新定义数组的每个属性,而数据是改写数组的7个数组方法:push,pop,shift,unshift,sort,splice,reverse二、实现原理vue在数据初始化时调用initData方法,然后通过new Observer对数据进行监测,然后对数据进行判断,如果是数组并且支持原型链就会执行prot
一、数组监听与对象监听的不同
对象的监听是直接递归使用Object.defineProperty重新定义数组的每个属性,而数据是改写数组的7个数组方法:push,pop,shift,unshift,sort,splice,reverse
二、实现原理
vue在数据初始化时调用initData方法,然后通过new Observer对数据进行监测,然后对数据进行判断,如果是数组并且支持原型链就会执行protoAugment让目标原型链指向arrayMethods,arrayMethods用来改写数组的原型方法。内部会采用函数劫持的方式,当用户调用这些方法(push,pop,shift,unshift,sort,splice,reverse)之后,还会调用原数组的方法进行更新数组。拿到原数组的方法,然后重新定义这些方法。
用户调方法时走的就是这个重写的mutator函数,这个函数还是会调用数组原有的方法,重写的mutator函数中会调用原生的方法,对新增数组的方法push,unshift,splice可以帮我们更新数组中的新增一项,对插入的数据使用observeArray再次进行监测,最后通过dep.notify通知视图更新。
源码:
1.在observer方法中
if (Array.isArray(value)) { // 如果是数组
if (hasProto) { // 判断是否支持原型链
protoAugment(value, arrayMethods) // arrayMethods就是改写数组的原型方法
} else {
copyAugment(value, arrayMethods, arrayKeys) // 如果没有原型链会走def方法添加__ob__属性
}
this.observeArray(value) // 深度监测数组中的每一个元素,遍历一遍数组,调用observe方法进行监测
} else {
this.walk(value)
}
2.protoAugment
function protoAugment (target, src: Object) {
target.__proto__ = src // 让目标的原型链指向src
}
3.arrayMethods
const arrayProto = 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(arrayMethods, method, function mutator (...args) { // 然后重新定义这些方法,用户调方法时走的就是这个mutator函数,这个函数还是会调用数组原有的方法
const result = original.apply(this, args) // 原生的方法
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args
break
case 'splice':
inserted = args.slice(2)
break // 新增数组的方法push,unshift,splice可以帮我们更新数组中的新增一项
}
if (inserted) ob.observeArray(inserted) // 对插入的数据再次进行监测,新增的数据也可能是对象类型
// observeArray是将数组遍历一遍
ob.dep.notify() // 通知视图更新
return result
})
})
4.observeArray
observeArray (items: Array<any>) { // 遍历数组,进行监测
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i]) // 必须是对象类型才能被监测,如果不是对象在oberve中会被return
// 如果数据已经是观察过的就不会在进行观测,会直接返回,避免进行重复监听
}
}
5.def方法其实就是Object.defineProperty
export function def (obj: Object, key: string, val: any, enumerable?: boolean) {
Object.defineProperty(obj, key, {
value: val,
enumerable: !!enumerable,
writable: true,
configurable: true
})
}
更多推荐
所有评论(0)