You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
if(typeofexpOrFn==='function'){this.getter=expOrFn;}else{this.getter=parsePath(expOrFn);if(!this.getter){this.getter=function(){};process.env.NODE_ENV!=='production'&&warn("Failed watching path: \""+expOrFn+"\" "+'Watcher only accepts simple dot-delimited paths. '+'For full control, use a function instead.',vm);}}
Watcher.prototype.get=functionget(){// 将全局的Dep.target设置成这个watch属性的watcherpushTarget(this);varvalue;varvm=this.vm;try{// 调用刚刚生成的getter函数,就是parsePath返回的那个函数// 这里把vm作为obj传入,所以会依次去读取vm.a vm.a.b vm.a.b.c 并且为这几个元素都收集了依赖。value=this.getter.call(vm,vm);}catch(e){if(this.user){handleError(e,vm,("getter for watcher \""+(this.expression)+"\""));}else{throwe}}finally{// "touch" every property so they are all tracked as// dependencies for deep watchingif(this.deep){// 如果watch的options里设置了deep,就递归的去收集依赖。traverse(value);}// 收集完毕,将Dep.target弹出栈popTarget();this.cleanupDeps();}returnvalue};
Dep.prototype.notify=functionnotify(){// stabilize the subscriber list firstvarsubs=this.subs.slice();for(vari=0,l=subs.length;i<l;i++){subs[i].update();}};
Watcher.prototype.getAndInvoke=functiongetAndInvoke(cb){varvalue=this.get();if(value!==this.value||isObject(value)||this.deep){if(this.user){try{cb.call(this.vm,value,oldValue);}catch(e){handleError(e,this.vm,("callback for watcher \""+(this.expression)+"\""));}}}};
上一篇介绍computed的文章讲到了,良好的设计对于功能的实现非常有帮助,computed的核心实现原理是计算watcher,那么watch其实也是基于watcher来实现的,我们还是从initWatch初始化看起。
initWatch
我们可以看到,对于用户定义的单个watch属性,最终vue调用了createWatcher方法
createWatcher
这段代码的开头对参数进行了规范化,因为watch是可以支持多种形式的。
最终调用了$watch,第一个参数是要观测的key或者'a.b.c'这样的表达式,handler是用户定义的回调函数,options是{deep: true}这样的watch配置
$watch
在vue中以$开头的api一般也提供给用户在外部使用,所以我们在外部也可以通过函数的方式去调用$watch, 比如
接下来我们来看看$watch的实现
可以看到, 在把options的user设为true以后,
调用了
我们看看这段函数进入Watcher以后会做什么
Watcher
进入了watcher的构造函数以后
这个watcher示例的user属性会被设置为true,
sync属性也会被设置为用户定义的sync 表示这个watcher的update函数会同步执行。
这时候我们的expOrFn应该是个key 或者 'a.b.c'这样的访问路径,所以会进入else逻辑。
首先看
parsePath
我们还是以a.b.c这个路径为例,
segments被以.号分隔成['a','b','c']这样的数组,
然后返回一个函数
这个函数接受一个对象 然后会依次去访问对象的.a 再去访问.a.b 再去访问.a.b.c,
其实这个的目的就是在访问的过程中为这些属性下挂载的dep去收集依赖。
回到我们的watcher的初始化,接下来执行的是
显然我们会走else逻辑,我们继续看this.get()
Watcher.prototype.get
至此为止,我们vm下的a a下的b b下的c都收集了这个watcher作为依赖,
那么当这些值中的任意值进行改变, 会触发他们内部dep.notify()
dep.notify
subs[i].update()其实就是调用了watcher的update方法,再回到watcher
watcher.update()
这个update是省略掉其他逻辑的,我们之前说过 如果watch的sync设置为true,
那么就会直接执行 this.run();
watcher.run
这里调用了getAndInvoke(this.cb),将我们定义的watch回调函数传入
watcher.getAndInvoke
其实就是做了个判断,如果上一次的值和这次的值不相等,或者deep为true,都会直接出发cb.call(this.vm),并且将新值和旧值传入,这就是我们可以在watch的回调函数里获取新值和旧值的来源。
至此watch函数的实现就分析完毕了,再次感叹一下,良好的设计是成功的开端啊!
The text was updated successfully, but these errors were encountered: