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()函数。希望这篇文章的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。