这个关于计算属性的问题挺有意思的。不仅检查了computed,还检查了vue的依赖收集和脏检查。computed:{foo(){if(this.a>0){returnthis.a}else{returnthis.b+this.c}}}data(){a:1,b:1,c:1,}众所周知,当a、b、c第一次全为1时,foo()的返回值为1。以foo()的返回值为1为初始状态,独立执行下面三个操作,Vue是怎么计算foo的?如果此时this.a=0,foo()如何计算呢?如果此时this.b=2,foo()如何计算呢?如果a的初始值为-1,执行this.a=1,foo()如何计算呢?目录执行性能源码分析createComputedGetterkeywatcher.jskeydep.js基于源码分析反汇编执行性能初始化a,b,c都为1,如何计算foo()?如果此时this.a=0,foo()如何计算呢?如果此时this.b=2,foo()如何计算呢?如果a的初始值为-1,执行this.a=1,foo()如何计算呢?一句话总结执行性能如果此时this.a=0,foo()是怎么计算的呢?如果此时this.b=2,foo()如何计算呢?如果a的初始值为-1,执行this.a=1,foo()如何计算呢?如果此时this.a=0,foo()如何计算呢?foo()的返回值会从this.a变成this.b+this.c,2。Vue会重新执行evaluate,得到返回值this.b+this.c。如果此时this.b=2,foo()如何计算呢?foo()的返回值还是this.a,1。Vue将跳过评估阶段并直接获取返回值this.a。如果a的初始值为-1,执行this.a=1,foo()如何计算呢?foo()的返回值将从this.b+this.c变为this.a。Vue会重新执行evaluate并得到返回值this.a。为什么会这样?执行评估的条件是什么?为什么a的初值可以-1再求值呢?源码分析对于this.b=2,Vue跳过evaluate阶段,直接获取返回值this.a。它是如何优化的?我们看一下源码:源码地址:state.jsComputed有3个很重要的函数:createComputedGetter(脏检查,依赖收集)keywatcher.js(Watcher)keydep.js(dependency)我们来看看最核心代码//脏检查,执行计算if(watcher.dirty){watcher.evaluate()}//Dep更新依赖if(Dep.target){watcher.depend()}下面看具体源码createComputedGetterdirtyCheck,重新计算检查__computedWatcher中的具体属性depupdatedependsfunctioncreateComputedGetter(key){returnfunctioncomputedGetter(){constwatcher=this._computedWatchers&&this._computedWatchers[key]if(watcher){//dirtyCheck,执行计算if(watcher.dirty){watcher.evaluate()}//Dep更新依赖if(Dep.target){watcher.depend()}returnwatcher.value}}}keywatcher.jsgetcollectiondependencyudpateUpdatedirtytotrue以便您可以重新评估evaluate以重新获得v计算属性的值dependNotifydepcollectiondependencyaddDepwatchersubscriptiondependencyexportdefaultclassWatcher{lazy:boolean;脏:布尔值;构造函数(){this.dirty=this.lazy//对于懒惰的watchers,dirty用于偷听this.value=this.lazy?undefined:this.get()//Dep的目标设置为foowatcher}//评估getter,并重新收集依赖项。get(){pushTarget(this)value=this.getter.call(vm,vm)返回值;}update(){if(this.lazy){this.dirty=true}}evaluate(){this.value=this.get()this.dirty=false}depend(){leti=this.deps.lengthwhile(i--){this.deps[i].depend()}}addDep(dep:Dep){constid=dep.idif(!this.depIds.has(id)){dep.addSub(this)}}}关键的dep.jsaddSub:添加订阅depend:添加依赖notify:通知更新exportdefaultclassDep{constructor(){this.subs=[]}addSub(sub:Watcher){this.subs.push(sub)}depend(){if(Dep.target){Dep.target.addDep(this)}}notify(){constsubs=this.subs.slice()for(leti=0,l=subs.length;i
