为何和Object对象监听方式不一样?
对象使用Object.defineProperty进行数据劫持监听;
defineProperty是Object原型上的,数组不能使用该方法;
思路:数据获取时收集依赖,数据变化更新时通知更新;
1.数组收集依赖;
// arr实际还是存在于object对象中,获取arr时就会触发object到get操作,从而实现依赖收集;
data(){
return {
arr:[1,2,3]
}
}
2.数组的通知依赖:重写操作数据的方法(pop/push/shift/unshift/sort/reverse/splice);
大致思路:
let arr = [1,2,3]
arr.push(4)
Array.prototype.newPush = function(val){
console.log('arr被修改了')
// 可以在这里做一些额外的操作;
this.push(val)
}
arr.newPush(4)
源码的实现:
1.通知依赖;
2.数组新增元素的侦测:对`push`、`unshift`、`splice`这3中方法分别处理,拿到新增的元素,再将其转化即可;
const arrayProto = Array.prototype
// 创建一个对象作为拦截器
export const arrayMethods = Object.create(arrayProto)
// 改变数组自身内容的7个方法
const methodsToPatch = [
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
/**
* Intercept mutating methods and emit events
*/
methodsToPatch.forEach(function (method) {
const original = arrayProto[method] // 缓存原生方法
def(arrayMethods, method, function mutator (...args) {
const result = original.apply(this, args)
const ob = this.__ob__
let inserted
switch (method) {
case 'push':
case 'unshift':
inserted = args // 如果是push或unshift方法,那么传入参数就是新增的元素
break
case 'splice':
inserted = args.slice(2) // 如果是splice方法,那么传入参数列表中下标为2的就是新增的元素
break
}
if (inserted) ob.observeArray(inserted) // 调用observe函数将新增的元素转化成响应式
ob.dep.notify() // 通知依赖
return result
})
})
- 不足之处:
// 以下两种方式处理数组无法监听数组变化;
let arr = [1,2,3]
arr[0] = 5; // 通过数组下标修改数组中的数据
arr.length = 0 // 通过修改数组长度清空数组
// 解决:`Vue`增加了两个全局API:
// 1.Vue.set( target, propertyName/index, value );
// 2.Vue.delete( target, propertyName/index );