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;i
2、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;i
3、watch
初始化watch,函数中会调用createWatcher,createWatcher会调用$watch,$watch调用newWatcher实例。
functioninitWatch(vm,watch){
for(varkeyinwatch){
varhandler=watch[key];
if(Array.isArray(handler)){
for(vari=0;i
2、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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。