Vue.js中数组变动的检测详解
前言
最近在尝试用Vue.js重构公司的现有业务代码,组件化的设计思路和MVVM的思想让我深深沉迷于其中。但是是踩到了不少坑,就比如这篇文章介绍的数组绑定后的更新检测。
相信大家都知道Observer,Watcher,vm可谓Vue中比较重要的部分,检测数据变动后视图更新的重要环节。在vue.js中$watch的用法示例中,我们讨论了如何实现基本的watch。
接下来,我们来看看如何实现数组变动检测。
例子:
//创建vm
letvm=newVue({
data:{
a:[{},{},{}]
}
})
//键路径
vm.$watch('a',function(){
//做点什么
})
思路
在js中,很容易实现hook,比如:
//hook一个console。log
let_log=console.log
console.log=function(data){
//dosometing
_log.call(this,data)
}
我们只要实现自定义的函数,就能观测到数组变动。
Vue.js中使用Object.create()这个函数来实现继承,从而实现自定义函数,以观测数组。
//简单介绍 vara=newObject();//创建一个对象,没有父类 varb=Object.create(a.prototype);//b继承了a的原型
继承
array.js定义如下:
//获取原型
constarrayProto=Array.prototype
//创建新原型对象
exportconstarrayMethods=Object.create(arrayProto)
//给新原型实现这些函数
[
'push',
'pop',
'shift',
'unshift',
'splice',
'sort',
'reverse'
]
.forEach(function(method){
//获取新原型函数(此时未实现undefined)
constoriginal=arrayProto[method]
//给新原型添加函数实现
Object.defineProperty(arrayMethods,method,{
value:functionmutator(){
leti=arguments.length
//获取参数
constargs=newArray(i)
while(i--){
args[i]=arguments[i]
}
//实现函数
constresult=original.apply(this,args)
//获取观察者
constob=this.__ob__
//是否更改数组本身
letinserted
switch(method){
case'push':
inserted=args
break
case'unshift':
inserted=args
break
case'splice':
inserted=args.slice(2)
break
}
//观察新数组
inserted&&ob.observeArray(inserted)
//触发更新
ob.dep.notify()
returnresult
},
enumerable:true,
writable:true,
configurable:true
})
})
ok,我们定义完了array.js,并作为模块导出,修改Observer的实现:
exportfunctionObserver(value){
this.dep=newDep()
this.value=value
//如果是数组就更改其原型指向
if(Array.isArray(value)){
value.__proto__=arrayMethods
this.observeArray(value)
}else{
this.walk(value)
}
}
//观测数据元素
Observer.prototype.observeArray=function(items){
for(leti=0,l=items.length;i<l;i++){
observe(items[i])
}
}
Observer修改完毕后,我们再看看Watcher,只需要改动其update函数,
Watcher.prototype.update=function(dep){
console.log('2.update')
constvalue=this.get()
constoldValue=this.value
this.value=value
if(value!==this.value||value!==null){
this.cb.call(this.vm,value,oldValue)
//如果没有此函数,会导致重复调用$watch回调函数。
//原因:数组变异过了,并对新数组也进行了观察,应该移除旧的观察者
dep.subs.shift()
}
}
结果:
constvm=newVue({
data:{
b:[{a:'a'},{b:'b'}]
}
})
vm.$watch('b',(val)=>{
console.log('------我看到你们了-----')
})
vm.b.push({c:'c'})
vm.b.pop({c:'c'})
vm.b.push({c:'c'})
//结果:
//console.log('------我看到你们了-----')
//console.log('------我看到你们了-----')
//console.log('------我看到你们了-----')
总结
至此,我们已经实现对数组变动的检测。主要使用了Object.create()函数。希望这篇文章的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。