vue中watch和computed为什么能监听到数据的改变以及不同之处
先来个流程图,水平有限,凑活看吧-_-||
首先在创建一个Vue应用时:
varapp=newVue({ el:'#app', data:{ message:'HelloVue!' } })
Vue构造函数源码:
//创建Vue构造函数 functionVue(options){ if(!(thisinstanceofVue) ){ warn('Vueisaconstructorandshouldbecalledwiththe`new`keyword'); } this._init(options); } //_init方法,会初始化data,watch,computed等 Vue.prototype._init=function(options){ varvm=this; //auid vm._uid=uid$3++; ...... //exposerealself vm._self=vm; initLifecycle(vm); initEvents(vm); initRender(vm); callHook(vm,'beforeCreate'); initInjections(vm);//resolveinjectionsbeforedata/props initState(vm); ...... };
在initState方法中会初始化data、watch和computed,并调用observe函数监听data(Object.defineProperty):
functioninitState(vm){ vm._watchers=[]; varopts=vm.$options; if(opts.props){initProps(vm,opts.props);} if(opts.methods){initMethods(vm,opts.methods);} if(opts.data){ initData(vm);//initData中也会调用observe方法 }else{ observe(vm._data={},true/*asRootData*/); } if(opts.computed){initComputed(vm,opts.computed);} if(opts.watch&&opts.watch!==nativeWatch){ initWatch(vm,opts.watch); } }
1、observe
observe在initState时被调用,为vue实例的data属性值创建getter、setter函数,在setter中dep.depend会把watcher实例添加到Dep实例的subs属性中,在getter中会调用dep.notify,调用watcher的update方法。
/** *Attempttocreateanobserverinstanceforavalue, *returnsthenewobserverifsuccessfullyobserved, *ortheexistingobserverifthevaluealreadyhasone. *该函数在initState中有调用 */ functionobserve(value,asRootData){ if(!isObject(value)||valueinstanceofVNode){ return } varob; if(hasOwn(value,'__ob__')&&value.__ob__instanceofObserver){ ob=value.__ob__; }elseif( shouldObserve&& !isServerRendering()&& (Array.isArray(value)||isPlainObject(value))&& Object.isExtensible(value)&& !value._isVue ){ ob=newObserver(value); } if(asRootData&&ob){ ob.vmCount++; } re*Observerclassthatisattachedtoeachobserved *object.Onceattached,theobserverconvertsthetarget *object'spropertykeysintogetter/settersthat *collectdependenciesanddispatchupdates. */ varObserver=functionObserver(value){ this.value=value; this.dep=newDep(); this.vmCount=0; def(value,'__ob__',this); if(Array.isArray(value)){ if(hasProto){ protoAugment(value,arrayMethods); }else{ copyAugment(value,arrayMethods,arrayKeys); } this.observeArray(value); }else{ this.walk(value); } }; /** *Walkthroughallpropertiesandconverttheminto *getter/setters.Thismethodshouldonlybecalledwhen *valuetypeisObject. */ Observer.prototype.walk=functionwalk(obj){ varkeys=Object.keys(obj); for(vari=0;i2、Dep
Watcher的update方法是在newDep的notify的方法中被调用的
/** *Adepisanobservablethatcanhavemultiple *directivessubscribingtoit. */ varDep=functionDep(){ this.id=uid++; this.subs=[]; }; //设置某个Watcher的依赖 //这里添加Dep.target,用来判断是不是Watcher的构造函数调用 //也就是其this.get调用 Dep.prototype.depend=functiondepend(){ if(Dep.target){ Dep.target.addDep(this); } }; //在该方法中会触发subs的update方法 Dep.prototype.notify=functionnotify(){ //stabilizethesubscriberlistfirst varsubs=this.subs.slice(); if(!config.async){ //subsaren'tsortedinschedulerifnotrunningasync //weneedtosortthemnowtomakesuretheyfireincorrect //order subs.sort(function(a,b){returna.id-b.id;}); } for(vari=0,l=subs.length;i3、watch
初始化watch,函数中会调用createWatcher,createWatcher会调用$watch,$watch调用newWatcher实例。
functioninitWatch(vm,watch){ for(varkeyinwatch){ varhandler=watch[key]; if(Array.isArray(handler)){ for(vari=0;i2、computed
初始化computed,调用newWatcher(),并通过defineComputed函数将计算属性挂载到vue实例上,使计算属性可以在模板中使用
varcomputedWatcherOptions={lazy:true} functioninitComputed(vm,computed){ //$flow-disable-line varwatchers=vm._computedWatchers=Object.create(null); //computedpropertiesarejustgettersduringSSR varisSSR=isServerRendering(); for(varkeyincomputed){ varuserDef=computed[key]; vargetter=typeofuserDef==='function'?userDef:userDef.get; //getter也就是computed的函数 if(getter==null){ warn( ("Getterismissingforcomputedproperty\""+key+"\"."), vm ); } if(!isSSR){ //createinternalwatcherforthecomputedproperty. watchers[key]=newWatcher( vm, getter||noop, noop, computedWatcherOptions ); } //组件定义的计算属性已在 //组件原型。我们只需要定义定义的计算属性 //在这里实例化。 if(!(keyinvm)){ defineComputed(vm,key,userDef); }else{ if(keyinvm.$data){ warn(("Thecomputedproperty\""+key+"\"isalreadydefinedindata."),vm); }elseif(vm.$options.props&&keyinvm.$options.props){ warn(("Thecomputedproperty\""+key+"\"isalreadydefinedasaprop."),vm); } } } } functiondefineComputed( target, key, userDef ){ varshouldCache=!isServerRendering();//true if(typeofuserDef==='function'){ sharedPropertyDefinition.get=shouldCache ?createComputedGetter(key) :createGetterInvoker(userDef); sharedPropertyDefinition.set=noop; }else{ sharedPropertyDefinition.get=userDef.get ?shouldCache&&userDef.cache!==false ?createComputedGetter(key) :createGetterInvoker(userDef.get) :noop; sharedPropertyDefinition.set=userDef.set||noop; } if(sharedPropertyDefinition.set===noop){ sharedPropertyDefinition.set=function(){ warn( ("Computedproperty\""+key+"\"wasassignedtobutithasnosetter."), this ); }; } Object.defineProperty(target,key,sharedPropertyDefinition); } //computed的getter函数,在模板获取对应computed数据时会调用 functioncreateComputedGetter(key){ returnfunctioncomputedGetter(){ varwatcher=this._computedWatchers&&this._computedWatchers[key]; if(watcher){ if(watcher.dirty){//true watcher.evaluate();//该方法会调用watcher.get方法,也就是computed对应的函数 } if(Dep.target){ watcher.depend(); } returnwatcher.value } } }通过以上代码可以看到watch和computed都是通过newWatcher实例实现数据的监听的,但是computed的options中lazy为true,这个参数导致它们走的是两条不同路线。
computed:模板获取数据时,触发其getter函数,最终调用watcher.get,也就是调用对应回调函数。
watch:模板获取数据时,触发其getter函数,将watcher添加到对应的Dep.subs中,在之后setter被调用时,Dep.notify通知所有watcher进行update,最终调用watcher.cb,也就是调用对应回调函数。
3、Watcher
构造函数在是watch时,会最后调用this.get,会触发属性的getter函数,将该Watcher添加到Dep的subs中,用于通知数据变动时调用。
调用Watcher实例的update方法会触发其run方法,run方法中会调用触发函数。其depend方法会调用newDep的depend方法,dep的depend会调用Watcher的addDep方法,最终会把该watcher实例添加到Dep的subs属性中
/** *观察者解析表达式,收集依赖项, *并在表达式值更改时激发回调。 *这用于$watch()api和指令。 */ varWatcher=functionWatcher( vm, expOrFn, cb, options, isRenderWatcher ){ this.vm=vm; ...... this.cb=cb;//触发函数 this.id=++uid$2;//uidforbatching this.active=true; this.dirty=this.lazy;//forlazywatchers ...... this.value=this.lazy?undefined?this.get();//computed会返回undefined,而watch会执行Watcher.get }; /** *Schedulerjobinterface. *Willbecalledbythescheduler. *该方法会执行触发函数 */ Watcher.prototype.run=functionrun(){ if(this.active){ varvalue=this.get(); if( value!==this.value|| //DeepwatchersandwatchersonObject/Arraysshouldfireeven //whenthevalueisthesame,becausethevaluemay //havemutated. isObject(value)|| this.deep ){ //setnewvalue varoldValue=this.value; this.value=value; if(this.user){ try{ this.cb.call(this.vm,value,oldValue); }catch(e){ handleError(e,this.vm,("callbackforwatcher\""+(this.expression)+"\"")); } }else{ this.cb.call(this.vm,value,oldValue); } } } }; /** *Evaluatethegetter,andre-collectdependencies. */ Watcher.prototype.get=functionget(){ pushTarget(this); varvalue; varvm=this.vm; try{ value=this.getter.call(vm,vm); }catch(e){ if(this.user){ handleError(e,vm,("getterforwatcher\""+(this.expression)+"\"")); }else{ throwe } }finally{ //"touch"everypropertysotheyarealltrackedas //dependenciesfordeepwatching if(this.deep){ traverse(value); } popTarget(); this.cleanupDeps(); } returnvalue }; /** *Subscriberinterface. *Willbecalledwhenadependencychanges. *在方法中调用Watcher的run方法 */ Watcher.prototype.update=functionupdate(){ /*istanbulignoreelse*/ if(this.lazy){ this.dirty=true; }elseif(this.sync){ this.run(); }else{ queueWatcher(this);//该方法最终也会调用run方法 } }; /** *Dependonalldepscollectedbythiswatcher.会调用newDep的depend方法,dep的depend会调用Watcher的addDep方法 */ Watcher.prototype.depend=functiondepend(){ vari=this.deps.length; while(i--){ this.deps[i].depend(); } }; /** *Addadependencytothisdirective. */ Watcher.prototype.addDep=functionaddDep(dep){ varid=dep.id; if(!this.newDepIds.has(id)){ this.newDepIds.add(id); this.newDeps.push(dep); if(!this.depIds.has(id)){ dep.addSub(this); } } };总结
以上所述是小编给大家介绍的vue中watch和computed为什么能监听到数据的改变以及不同之处,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。