(以下都是简化的代码,实际的源码复杂许多)首先是让vue本身对data引用,以及添加$watch方法:
@H_502_0@importWatcherfrom'../watcher' import{observe}from"../observer" exportdefaultclassVue{ constructor(options={}){ this.$options=options letdata=this._data=this.$options.data //shallowiteratethroughkeystogivevuedirectreference Object.keys(data).forEach(key=>this._proxy(key)) //deepiteratethroughallkeystoproxyallkeys observe(data,this) } $watch(expOrFn,cb,options){ newWatcher(this,expOrFn,cb) } _proxy(key){ varself=this Object.defineProperty(self,key,{ configurable:true,enumerable:true,get:functionproxyGetter(){ returnself._data[key] },set:functionproxySetter(val){ self._data[key]=val } }) } }接着实现深度代理函数observe,递归代理所有属性,从而监测所有属性的变化:
@H_502_0@import{def}from"../util" importDepfrom"./dep" exportdefaultclassObserver{ constructor(value){ this.value=value this.walk(value) } //deepobserveeachkey walk(value){ Object.keys(value).forEach(key=>this.convert(key,value[key])) } convert(key,val){ defineReactive(this.value,val) } } exportfunctiondefineReactive(obj,val){ vardep=newDep() //recursiveobserveallkeys varchildOb=observe(val) Object.defineProperty(obj,{ enumerable:true,configurable:true,get:()=>{ //checkdependencytosubscribe if(Dep.target){ dep.addSub(Dep.target) } returnval },set:newVal=>{ varvalue=val if(newVal===value){ return } val=newVal //checkchangetoreobserveandpublishalldependencies childOb=observe(newVal) dep.notify() } }) } exportfunctionobserve(value,vm){ if(!value||typeofvalue!=='object'){ return } returnnewObserver(value) }实现依赖处理:
实现watch:
以上示例是可以无依赖直接运行的。
接下来是vue的源码片段;
一般对象的处理可以直接代理defineProperty就可以了,不过对于Array的各种操作就不管用了,所以vue进行了基本数组方法代理:
@H_502_0@functiondef(obj,val,enumerable){ Object.defineProperty(obj,{ value:val,enumerable:!!enumerable,writable:true,configurable:true }) } functionindexOf(arr,obj){ vari=arr.length while(i--){ if(arr[i]===obj)returni } return-1 } constarrayProto=Array.prototype exportconstarrayMethods=Object.create(arrayProto) ;[ 'push','pop','shift','unshift','splice','sort','reverse' ] .forEach(function(method){ //cacheoriginalmethod varoriginal=arrayProto[method] def(arrayMethods,method,functionmutator(){ //avoidleakingarguments: //http://jsperf.com/closure-with-arguments vari=arguments.length varargs=newArray(i) while(i--){ args[i]=arguments[i] } varresult=original.apply(this,args) varob=this.__ob__ varinserted switch(method){ case'push': inserted=args break case'unshift': inserted=args break case'splice': inserted=args.slice(2) break } //sameasobjectchange //checkchangetoreobserveandpublishalldependencies if(inserted)ob.observeArray(inserted) //notifychange ob.dep.notify() returnresult }) }) //es5defineProperty对于数组的某些操作和属性(如:length)变化代理有问题,所以需要使用定义的方法操作 具体原因看http://www.cnblogs.com/ziyunfei/archive/2012/11/30/2795744.html和 http://wiki.jikexueyuan.com/project/vue-js/practices.html def( arrayProto,'$set',function$set(index,val){ if(index>=this.length){ this.length=Number(index)+1 } returnthis.splice(index,1,val)[0]//在里面还是通过代理方法splice实现 } ) def( arrayProto,'$remove',function$remove(item){ /*istanbulignoreif*/ if(!this.length)return varindex=indexOf(this,item) if(index>-1){ returnthis.splice(index,1) } } )
看实际的observer干了什么:
总的来说,vue是代理了原始数据的增删改查,从而进行事件订阅和发布等操作,从而控制数据流。
heavily inspired by:https://segmentfault.com/a/1190000004384515