当前位置: 首页 > Web前端 > vue.js

Vue2源码-computed和watch的实现

时间:2023-03-31 23:11:11 vue.js

computed首先会在Vue实例上有一个_computedWatchers属性,然后以每个computed属性为键值一个一个生成watcher对象。通常有两种使用计算的形式。一种是直接返回一个计算函数,另一种是使用object对象定义一个计算函数对应的get属性方法。总之就是搞定这个计算函数。if(!isSSR){watchers[key]=newWatcher(vm,getter||noop,noop,computedWatcherOptions);服务端渲染暂不考虑,主要看Watcher构造函数的参数,当前Vue实例,getter是前面的计算函数,第三个参数不需要传空函数,computedWatcherOptions对应{lazy:真的}。然后依次通过defineComputed方法在实例上定义计算属性,仍然使用Object.definePropertyAPI。在computed中使用computed属性时,进入定义的getter方法。通过属性值,可以在上面提到的_computedWatchers中找到对应的watcher。functioncreateComputedGetter(key){returnfunctioncomputedGetter(){varwatcher=this._computedWatchers&&this._computedWatchers[key];如果(watcher){如果(watcher.dirty){watcher.evaluate();}if(Dep.target){watcher.depend();}returnwatcher.value}}}脏值在watcher初始化的时候被lazy赋值给true,调用watcher上的evaluate方法。这个方法就是调用watcher的get方法,然后dirty就变成false了。当执行get方法时,会调用计算属性对应的watcher中的计算函数。通常,使用计算。通常,计算属性是依赖于其他数据来计算的。因此,调用computed属性函数时,也会触发其他数据(比如data中定义的一些数据)的get方法,从而使当前watcher(computedwatcher)收集其他数据对应的dep依赖。执行完evaluate后,Dep.target变回了outerwatcher,再次调用watcher.depend方法存储computed中watcher依赖outerwatcher的dep。这样,即使data中的数据只是在computed中被引用,对应的dep也会存在于outerwacter中。update阶段更新data中的值,父组件watcher执行update方法将更新操作推入异步队列,被lazy属性判断为对应computed属性的watcher只需要改变dirty再次回到true,当引用到的时候重复上面的步骤进行计算即可。综上所述,computed的每个属性都会单独创建一个watcher对象,然后computed的属性函数依赖于其他数据(一般在data中)。对应使用该函数时,会触发对其依赖数据的依赖收集,watcher.depend会依赖添加到当前Vue实例的watcher中。当依赖数据发生变化时,只需要在computed对应的watcher中开启dirty状态,使用时重新触发计算函数即可。手表类似于计算。watch也定义了一个对象,循环watch对象,key值对应要监控的值。属性值分为数组和非数组类型。createWatcher处理watch的行为。也可以通过整个方法看到,watchlistenerfunction如果是对象,则response函数在handler属性上;如果直接是字符,则取实例中对应的methods方法;最后直接定义为函数使用。functioncreateWatcher(vm,expOrFn,handler,options){if(isPlainObject(handler)){options=handler;handler=handler.handler;}if(typeofhandler==='string'){handler=vm[handler];}returnvm.$watch(expOrFn,handler,options)}然后观察实例上的$watch方法Vue.prototype.$watch=function(expOrFn,cb,options){varvm=this;如果(isPlainObject(cb)){returncreateWatcher(vm,expOrFn,cb,options)}options=options||{};options.user=true;varwatcher=newWatcher(vm,expOrFn,cb,options);如果(options.immediate){try{cb.呼叫(虚拟机,观察者。值);}catch(error){handleError(error,vm,("立即观察者的回调\""+(watcher.expression)+"\""));}}返回函数unwatchFn(){watcher.teardown();}}实现的核心就是这个方法,类似于computed。主要实现是Watcher构造函数,watcher实例放在vm._watchers中。看这里不一样的地方,配置user为true,这里的第二个参数expOrFn是要监听的数据对应的一个字符,返回一个函数通过parsePath获取数据,在watcher上调用get方法,dep对应到data触发依赖收集,会把watcher保存到dep.subs中,update阶段和data中的data基本一样,触发dep中所有watcher更新,最后执行run方法,包裹一层oftrycatchuser属性外,防止报错终止程序。其他的,如果immediate配置为ture,则直接调用一次监听函数。最后返回一个函数。如果执行该函数,会调用watcher的teardown方法,将watcher实例从当前vm和data依赖中移除,但是目前还没有找到这个解绑方法的具体用法。