浅析vue 函数配置项watch及函数 $watch 源码分享
Vue双向榜单的原理
大家都知道Vue采用的是MVVM的设计模式,采用数据驱动实现双向绑定,不明白双向绑定原理的需要先补充双向绑定的知识,在watch的处理中将运用到Vue的双向榜单原理,所以再次回顾一下:
Vue的数据通过Object.defineProperty设置对象的get和set实现对象属性的获取,vue的data下的数据对应唯一一个dep对象,dep对象会存储改属性对应的watcher,在获取数据(get)的时候为相关属性添加具有对应处理函数的watcher,在设置属性的时候,触发def对象下watcher执行相关的逻辑
//为data的的所有属性添加getter和setter
functiondefineReactive(obj,key,val,customSetter,shallow
){
//
vardep=newDep();
/*....省略部分....*/
varchildOb=!shallow&&observe(val);//为对象添加备份依赖dep
Object.defineProperty(obj,key,{
enumerable:true,
configurable:true,
get:functionreactiveGetter(){
varvalue=getter?getter.call(obj):val;
if(Dep.target){
dep.depend();//
if(childOb){
childOb.dep.depend();//依赖dep添加watcher用于set,array改变等使用
if(Array.isArray(value)){
dependArray(value);
}
}
}
returnvalue
},
set:functionreactiveSetter(newVal){
varvalue=getter?getter.call(obj):val;
/*eslint-disableno-self-compare*/
if(newVal===value||(newVal!==newVal&&value!==value)){
return
}
/*eslint-enableno-self-compare*/
if("development"!=='production'&&customSetter){
customSetter();
}
if(setter){
setter.call(obj,newVal);
}else{
val=newVal;
}
childOb=!shallow&&observe(newVal);
dep.notify();//有改变触发watcher进行更新
}
});
}
在vue进行实例化的时候,将调用initWatch(vm,opts.watch);进行初始化watch的初始化,该函数最终将调用vm.$watch(expOrFn,handler,options)进行watch的配置,下面我们将讲解vm.$watch方法
Vue.prototype.$watch=function(
expOrFn,
cb,
options
){
varvm=this;
if(isPlainObject(cb)){
returncreateWatcher(vm,expOrFn,cb,options)
}
options=options||{};
options.user=true;
//为需要观察的expOrFn添加watcher,expOrFn的值有改变时执行cb,
//在watcher的实例化的过程中会对expOrFn进行解析,并为expOrFn涉及到的data数据下的def添加该watcher
varwatcher=newWatcher(vm,expOrFn,cb,options);
//immediate==true立即执行watchhandler
if(options.immediate){
cb.call(vm,watcher.value);
}
//取消观察函数
returnfunctionunwatchFn(){
watcher.teardown();
}
};
来看看实例化watcher的过程中(只分享是观察函数中的实例的watcher)
varWatcher=functionWatcher(
vm,
expOrFn,
cb,
options,
isRenderWatcher
){
this.vm=vm;
if(isRenderWatcher){
vm._watcher=this;
}
vm._watchers.push(this);
//options
if(options){
this.deep=!!options.deep;//是否观察对象内部值的变化
this.user=!!options.user;
this.lazy=!!options.lazy;
this.sync=!!options.sync;
}else{
this.deep=this.user=this.lazy=this.sync=false;
}
this.cb=cb;//观察属性改变时执行的函数
this.id=++uid$1;//uidforbatching
this.active=true;
this.dirty=this.lazy;//forlazywatchers
this.deps=[];
this.newDeps=[];
this.depIds=new_Set();
this.newDepIds=new_Set();
this.expression=expOrFn.toString();
//parseexpressionforgetter
if(typeofexpOrFn==='function'){
this.getter=expOrFn;
}else{
//将需要观察的数据:string|Function|Object|Array等进行解析如:a.b.c,并返回访问该表达式的函数
this.getter=parsePath(expOrFn);
if(!this.getter){
this.getter=function(){};
"development"!=='production'&&warn(
"Failedwatchingpath:\""+expOrFn+"\""+
'Watcheronlyacceptssimpledot-delimitedpaths.'+
'Forfullcontrol,useafunctioninstead.',
vm
);
}
}
//this.get()将访问需要观察的数据
this.value=this.lazy
?undefined
:this.get();
};
/**
*Evaluatethegetter,andre-collectdependencies.
*/
Watcher.prototype.get=functionget(){
//this为$watch方法中实例化的watcher
pushTarget(this);讲this赋给Dep.target并缓存之前的watcher
varvalue;
varvm=this.vm;
try{
//访问需要观察的数据,在获取数据的getter中执行dep.depend();将$watch方法中实例化的watcher添加到对应数据下的dep中
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();//将之前的watcher赋给Dep.target
this.cleanupDeps();
}
returnvalue
};
Watcher.prototype.run=functionrun(){
/*....省略部分....*/
varvalue=this.get();//重新获取info的值
varoldValue=this.value;//保存老的值
this.value=value;
this.cb.call(this.vm,value,oldValue);//执行watch的回调
/*....省略部分....*/
};
以上代码在watcher实例化的时候执行 this.getter=parsePath(expOrFn);返回一个访问该属性的函数,参数为被访问的对象 如vm.$watch("info",function(new,old){console.log("watchsuccess")});,this.getter=function(obj){returnobj.info};,在执行watcher的get方法中,将执行value=this.getter.call(vm,vm);,触发属性的get方法,添加该watcher至info属性对应的def对象中,如果需要深度监听,将执行traverse(value),依次访问info(假设info只对象)对象下的属性,如果info的属性还有是对象的属性,将进行递归访问,以达到info以及info下所有的属性的def对象都会添加该watcher实例。
当我们执行vm.info="change"时,将出发info的set方法,执行dep.notify();出发info所依赖的watcher执行watcher的run方法,即实现监听
总结
以上所述是小编给大家介绍的vue函数配置项watch及函数$watch源码分享,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!