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
letuid=0/** * A watcher parses an expression, collects dependencies, * and fires callback when the expression value changes. * This is used for both the $watch() api and directives. */exportdefaultclassWatcher{vm: Component;expression: string;cb: Function;id: number;deep: boolean;user: boolean;lazy: boolean;sync: boolean;dirty: boolean;active: boolean;deps: Array<Dep>;newDeps: Array<Dep>;depIds: SimpleSet;newDepIds: SimpleSet;before: ?Function;getter: Function;value: any;constructor(vm: Component,expOrFn: string|Function,cb: Function,options?: ?Object,isRenderWatcher?: boolean){this.vm=vmif(isRenderWatcher){vm._watcher=this}vm._watchers.push(this)// optionsif(options){this.deep=!!options.deepthis.user=!!options.userthis.lazy=!!options.lazythis.sync=!!options.syncthis.before=options.before}else{this.deep=this.user=this.lazy=this.sync=false}this.cb=cbthis.id=++uid// uid for batchingthis.active=truethis.dirty=this.lazy// for lazy watchersthis.deps=[]this.newDeps=[]this.depIds=newSet()this.newDepIds=newSet()this.expression=process.env.NODE_ENV!=='production'
? expOrFn.toString()
: ''// parse expression for getterif(typeofexpOrFn==='function'){this.getter=expOrFn}else{this.getter=parsePath(expOrFn)if(!this.getter){this.getter=noopprocess.env.NODE_ENV!=='production'&&warn(`Failed watching path: "${expOrFn}" `+'Watcher only accepts simple dot-delimited paths. '+'For full control, use a function instead.',vm)}}this.value=this.lazy
? undefined
: this.get()}/** * Evaluate the getter, and re-collect dependencies. */get(){pushTarget(this)letvalueconstvm=this.vmtry{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){traverse(value)}popTarget()this.cleanupDeps()}returnvalue}/** * Add a dependency to this directive. */addDep(dep: Dep){constid=dep.idif(!this.newDepIds.has(id)){this.newDepIds.add(id)this.newDeps.push(dep)if(!this.depIds.has(id)){dep.addSub(this)}}}/** * Clean up for dependency collection. */cleanupDeps(){leti=this.deps.lengthwhile(i--){constdep=this.deps[i]if(!this.newDepIds.has(dep.id)){dep.removeSub(this)}}lettmp=this.depIdsthis.depIds=this.newDepIdsthis.newDepIds=tmpthis.newDepIds.clear()tmp=this.depsthis.deps=this.newDepsthis.newDeps=tmpthis.newDeps.length=0}/** * Subscriber interface. * Will be called when a dependency changes. */update(){/* istanbul ignore else */if(this.lazy){this.dirty=true}elseif(this.sync){this.run()}else{queueWatcher(this)}}/** * Scheduler job interface. * Will be called by the scheduler. */run(){if(this.active){constvalue=this.get()if(value!==this.value||// Deep watchers and watchers on Object/Arrays should fire even// when the value is the same, because the value may// have mutated.isObject(value)||this.deep){// set new valueconstoldValue=this.valuethis.value=valueif(this.user){constinfo=`callback for watcher "${this.expression}"`invokeWithErrorHandling(this.cb,this.vm,[value,oldValue],this.vm,info)}else{this.cb.call(this.vm,value,oldValue)}}}}/** * Evaluate the value of the watcher. * This only gets called for lazy watchers. */evaluate(){this.value=this.get()this.dirty=false}/** * Depend on all deps collected by this watcher. */depend(){leti=this.deps.lengthwhile(i--){this.deps[i].depend()}}/** * Remove self from all dependencies' subscriber list. */teardown(){if(this.active){// remove self from vm's watcher list// this is a somewhat expensive operation so we skip it// if the vm is being destroyed.if(!this.vm._isBeingDestroyed){remove(this.vm._watchers,this)}leti=this.deps.lengthwhile(i--){this.deps[i].removeSub(this)}this.active=false}}}
watch
initWatch在initState里面执行了初始化,把vm跟写的watch对象传了进去
然后在initWatch里面做了一些写法判断
从这里可以看出我们watch可以写成一个数组格式
然后都是调用了
createWatcher(vm, key, handler)
在
createWatcher
里面做了handler
是对象或者字符串处理,最终调用vm.$watch(expOrFn, handler, options)
然后又对手写的$watch的cb做了参数判断,之后在options添加user标识为用户watcher,最终执行new Watcher()
在Watcher类里面,如果是通过$watch执行的new Watcher,标识this.user为true,然后判断expOrFn,这是$watch传过来的key值,如果他不是一个函数就会执行parsePath方法解析路径,最终赋值给this.getter
就是通过split弄出数组然后循环去取值,最后执行this.get赋值给this.value,this.get方法就是执行this.getter方法然后返回结果value,这样this.value就是得到当前watch的值,在执行this.getter方法的时候就会触发值的依赖收集,那么该值就会收集当前的 user Watcher,当该值变化时,就会触发watcher的run方法,
然后执行this.get方法,得到新的value值,最后执行
invokeWithErrorHandling(this.cb, this.vm, [value, oldValue], this.vm, info)
这个里面就是执行this.cb方法,然后做一些错误处理,在执行this.cb方法的时候把this.value跟value,传了进去,这样我们在写watch的时候就能拿到新值旧值了,
然后还有watch的选项deep跟immediate,deep就是在执行this.get的时候,如果设置了deep为true,就会执行
traverse(value)
去循环访问就会去进行依赖收集,所以对象里面有数据改动就会重新执行,然后immediate设置为true的时候,在执行$watch的时候就会直接执行
invokeWithErrorHandling(cb, vm, [watcher.value], vm, info)
The text was updated successfully, but these errors were encountered: