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

如何理解vue的computed?

时间:2023-04-01 00:46:17 vue.js

这个关于计算属性的问题挺有意思的。不仅检查了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;i0){returnthis.a}else{returnthis.b+this.c}}}data(){a:1,b:1,c:1,}created(){this.b=2;}初始化a、b、c都为1时,foo()是怎么计算的呢?初始化watcher,创建getter获取值并设置dirty为falsewatcher帮助dep收集依赖,就是this.adependencycollectiongraphinitializewatcher_computedWatchers:{foo:Watcher{(vm,getter,null,{lazy:true})}}//watcherWatcher:{lazy:true,dirty:true,value:undefined,deps:[]}创建getter获取value并将dirty设置为false//Watcher:{lazy:true,dirty:true,value:undefined}constwatcher=this._computedWatchers&&this._computedWatchers[key]if(watcher){//脏检查,执行计算if(watcher.dirty){watcher.evaluate()//获取值,将脏设置为false}//返回这。a1returnwatcher.value}//watcher.evaluate()disassembleevaluate(){//从foo的getter获取值get():this.a1this.value=this.get()//changedirtyisfalsethis.dirty=false}执行后结果为Watcher{lazy:true,dirty:false,value:this.a,deps:[Depa]}watcher帮助dep收集依赖if(watcher){//Dep更新依赖if(Dep.target){watcher.depend()}}//watcher.depend()反汇编depend(){leti=this.deps.lengthwhile(i--){this.deps[i].depend()}}//dep.depend()反汇编depend(){if(Dep.target){Dep.target.addDep(this)}}//watcher.addDep反汇编addDep(dep:Dep){constid=dep.idif(!this.depIds.has(id)){dep.addSub(this)}}//dep.addSub()反汇编addSub(sub:Watcher){this.subs.push(sub)}最终结果为:只计算属性foo收集this.a因为dep不收集b和c。Watcher{lazy:true,dirty:false,value:this.a,deps:[Depa]}依赖集合图(dirtyisfalse)deps:[Depa(1)]如果此时this.a=0,foo()如何计算?执行computedGetter会触发watcher.evaluate()。在a的集合的基础上,再次收集b和c的依赖集合图。当我们执行this.a=0时,a的setter发送依赖更新,getter执行更新。脏是由假变成真的。由于dirty为true,执行evaluate得到foo()的返回值this.b+this.c。//发出依赖更新notify(){constsubs=this.subs.slice()for(leti=0,l=subs.length;i0){returnthis.a}//b和c的get()触发,采集depselse{returnthis.b+this.c}}}data(){a:-1,b:1,c:1,}如何采集?get(){pushTarget(this)value=this.getter.call(vm,vm)}这时候又触发了this.a=1。由于收集了this.a的依赖,可以直接触发更新。最终返回1。Dependencycollectiongraph(dirtyistrue)deps:[Depa(1),Depa(2),Depa(2)]一句话,在一个computedproperty中,每次像this.foo这样的调用都会在get()重新收集依赖项。当依赖集合不止一次(不是一次)时,它被认为是一个脏(dirty)的计算属性,需要在取值之前重新评估。对于干净的计算属性,不需要重新执行evaluate,Vue可以直接取值。期待与您交流,共同进步:微信公众号:哒哒哒前端/excellent_developers前端问答互助星球:t.zsxq.com/yBA2Biq努力成为一名优秀的前端工程师!