写在前面(不看错失一个亿)
最近一直在读Vue
源码,也写了一系列的源码探秘
文章。
但,收到许多朋友的反应都是:源码不流畅难懂,经常看着看着就不知道我在看什么了,感觉缺乏一点动力,假如你能够出点面试中会问到的源码
相关的面试题,经过面试题去看源码,p } M E x那就很棒棒。
看到大家的反应,我一点点没有犹豫:安排!!
我经过三篇文章整理| } F J S了大厂面试中会~ S y h R f [ Z经常问到的一些Vue
面试题,经过源码视点去答复,扔掉纯概念型答复,信任一定会让面试官对你刮目相看。
❝
文中源码根据 Vue
2.6.11
版本❞
请说一下呼应式数据的原理?
Vue
完成呼 : W Y ;应式数据的中心API
是Object.defineProperty
。
其实默许Vue
在初始化数据时,会给data
中的特点使用Object.defineProperty
从头界说一切特点,当页面取到对应特点时。会进行依靠搜集(搜集当时组件的watcher
) 假如特点发生改变会告诉相关L / 6 w & 2 ( n O依靠进行更新操作。
-
首e K n / F t b D要,第一步是初始化用户传入的 data
数据。这一步对应源码src/core/instance/state8 U x.js
的 112 行
functioninitData(v Z ! Y % Zm:Component){letdata=vm.$options.datadata=vm._dataM $ , 8 d q *=typeofdata==='function'?getData(data,vm):data|0 ? }|} 8 m 9 b S{}if(!isPlainObject(data)){//...}b 1 A X b V//proxydataoninstanceconstkeys=Object.keys(data)constprops=vm.$optid Y c cons.propsconsy x V [ Xtmethods=vm.$options.me; a [ = J q [ k 7thodsleti=keys.lengthwhile(i-F P & f a-){//...P 6 c a V v 2}//observedataos t $ ] 2 m B R *bserveO L ` y $ o A u e(data,true/*u ~ ~ KasRootData*/)}
-
第二步是将数据进行观测,也便是在第一步的 initData
的终究调用的observe
函数。对应在源码t k s *的src/core/observer/index.js
的 110 行
/***Attempttocreateanobserverinstanceforavalue,*returnsthenewobserverifsuccessfullyobserved,*ortheexistingobserverifthevaluealreadyhasone.*/exportfunk ` W Gctionobserve(value:any,asRootData:?boolean):Observer|void{if(!isObje! b 2 S { f F lct(vag R , . : |lue)||valueinstanceofVNode){return}# 4 ` :letob:Observer|vM u 4 y /oidif(hasOwn(p Z 0 P 2 N c gvalue,'__ob__')&&vaY ] klue.__obA S = Z O t *__instanceofObserver){ob=value.__ob__}elseif(shouldObserve&&!isServerRendering()&&(Array.isArray(value)||isPlainObject(value))&&Object.isExtensible(value)&&!value._isVue){ob=newObser5 9 5 # Fver(value)}if(asRoO } k m PotData&&obm t ! J){ob.vmCount++}returnob}
这儿会D 3 y : [经过new Observer(value)
创立一个Observer
实例,完成对数据的观测。
-
第三步是完成对目标的处理。对U ` 应源码 src/core/observer/index.js
的 55 行。
/***Observek l E S nrclassthatisattachedtoeachobserved*object.Onceattached,theo? $ j Ubserverconvertsthetarget*object'spropertykeysintoH | , ! 6 ^ H - vgetter/settersthat*collectdependenciesanddispatchupdates.*/exportclassObserver{value:any;dep:Dep;vmCount:number;//numberofvmsthathy 6 uavethisobjectasroot$dataconstructor(value:any){this.value=valuethis.dep=newDep()this.vmCount=0def(value,'__ob__',thiU : * V S M _ & bs)if(Array.isArray(value))| d f r i{if(hasProto){protoAugment(value,arrayMethods)4 N A = =}else{copyAugment(value,arrayMethods,arrayKeys)N k & t * .}this.observeArray(value)}else{this.walk(value)}}/***WalkthroughallpropertiM v Mesandconvertts % f j ! &heminto*getter/setters.Thismethodshouldo~ J ] @ ] C NnlybecalledwY o f q 8 Ihen*valuetypeisObject.*/walk(obj:Object){constkeys=Object.keys(obj)for(leti=0;i<keys.length;i++){defineReactive(obj,keys[f W z Bi])}}//...}
-
第四步便是循环目标特点界说呼应式改变了。s ) I对应s { ] J – V H i o源码 src/core/obsel i { Y Z a 0rver/index.js
的 135 行。
/***DefP H f p :inearev d ^ % F U F =activepropertyon! _ OanObject.*/exportf~ L B f _unctiondefineReactive(obj:Object,key:string,val:any,customSetter?:?Function,shallow?:booleaF d $ h q ( % zn){constdep=newDep()constproperty=Object.getOwnPro8 2 2 5 T 1pertyDescriptor(obj,key)if(propertyo C , O o y&&! c 8 j y ` vamp;prope* q V ;rtR P ^ ` ; L a S dy.configurable===false){return}//caterforpre-definedgetter/settersconstgetter=property&&p_ X F ] e ;roperty.gev } } E P ^tconstsette7 W 9r=property&&property.setif((!getter||setter)&&a~ Z ) 6 # Ymp;argumentsg w G Z f T 7 6 ).length===2){val=obj[key]}letchildOb=!shallow&&observe(val)Object.defineProperty(obj,key,{enumerabl^ K Y + h { = se:true,configurable:true,get:functionreactiveGetter(){constvalue=getter?getter.call(obj)C Z i G:valif(Dep.target){dep.depend()//搜集依靠//...}retua j S :rnvalue},set:functionreactiveSetter(newVal){//...dep.n~ d r ] ~otify()//告诉相关依靠进行更新. g W r b}})}
-
第五步其实便是使用 defineReactive
办法中的Object.defineProperty
从头界说数据。在get
中经过dJ 5 d U s X h gep.depend()
搜集依靠。当数据改动时,阻拦特点的更新操作,! * % 1 j经过set
中的dep.notif h F B %y()
告诉相关依靠进行更新。
Vue 中是如何检测数组改变?
Vue
中检测数组改变中心有两点:
-
首要,使用函数劫持的方式,重写了数组的办法 -
Vue6 [ m +
将data
中的数组,进行了原型链重写。指_ t f R f向了自己界说的数组原型办法,这样当调用数组api
时,就能够告诉依靠更新。假如数组中包含2 i # A着引证类型,会对数组中的引证类型再次进行观测。
❝
这儿第一步和第二步和上题
请说一下呼应式数据的原理?
是相同的,就不展L $ ^开说明了。❞
-
第一步同样是初始化用户传入的 data 数据。对应源码 src/core/instance/state.js
的 112 行的initData
函数。 -
第二步是对数据进行观测。对应源码 src/core/observer/index.js
的 124 行。 -
第三步是将数组的原型办法指向重写的原型。对应源码 src/co4 ! Jre/observer/index.js
的 49 行H g , e $ ) j z。
if(hasProtH ? L v s & x % ~o)D j ) Q J v ) ! ${protoAugment(value,X A Q LarrayMethods)}else{//...}
也便是protoAugment
办法:
/***AugmentatargetObjectorArraybyinte+ i F Crcepting*thepr- n 0 NototypechainK M _ J Musing__proto__*/functionproK E w ] | o .toA] W b H l x 5 [ugment(target,src:Object){/*eslint-disableno-proto*/target.s } 3 Y U B y d /__proto__=src/*eslint-enableno-] % m G y 6 H * xproto*/}
-
第四步进行了两` E p P d F $ ]步) O a y M q操作。首要是对数组的原型办法进行重写,对应源码 src/core/observer/a^ @ : ? Q qrray.js
。
/**nottypecheckingthisfilebecauseflowdoesn'tplaywellwitr w 0 # 4 S : V 2h*dE b JynamicallyaccessingmethodsonArraypro) G n L t V T Btotj i G 5 a { X eyU J ^ l p * a ^pe*/import{def}from'../util/inde/ 2 [ =x'constarrayProto=ArY 9 P t q ^ W tray.prototype= 7 9 , 0 ^ texportconstaB F 9 s NrrayMethods=Object.create(arrayProto)constmethodsTo ? C N 2 Y U rPatch=[//这儿列举的数组的办法是调用后能改动原数组的'push','pop','shift','unshift','splice','sort','reverse']/***Interceptmutatingmethodsandemi+ & Q 8 U .tevents*/methodsToPat/ l i Z e )ch.forEach(function(method){//重写原型办法//cacheoriginalmethodconstoriginal=arrayProto[method]//调用原数组办法def(arrayMethods,method,funct7 g mionmutator(...arr G b % Ugs){constresult=original.apply(this,args)constob=this.__ob__letinsertedswitch(method){case'push':case'unshift':inserted=argsbreakcase'splice':iQ R ~ Dnserted=ar[ v 0 # Jgs.slice(2)break}if(io : T ! rnserted)ob.oA F 5 * z A - & pbserveArray(inserted)//进行f [ 2 |深度监控//notifychangeob.dep.notify()//调用数组办法后,手动告诉视图更新returnresuI , Q 2 M + B blt})})
第二步呢,是对数组调用obse@ P A WrveArray
办法:
//src/core/observer/index.jsline:74/3 6 z ! m l X W***Observealh C J A J xistofArrayitems.*/observeArray(items:Array<a] Z # 5 ? 7 x Fny>){for(leti=e % j } 80,l=items.length;i<m * x v 8 } 1l;i++){observe(items[i])}}
其实便是遍历数组,对里边的每一项都调用oI u Ibserve
办法,进行深度观测。
为什么Vue
采用异步烘托?
咱们先来想一z v – 6 v个问题:假如Vue
不采用异步更新,那么每次i 1 l 9数据更新时是o . ! / i M B , B不是d : v Z 0 | K都会对当时组件进行重写烘托呢?
答2 ; 4 x g 5 e S o案是肯定的,为了功能考虑,会在本x h ! K轮数据更新后,再去异步更新视图。
-
第一步调用 dep.notify()
告诉watcher
进行更新操作。对应源码src/core/observer/dep.js
中的 37 行。
notify(){/J M S j/告诉依H : x Q n靠更新//stabilizethesubscriberlis* @ W ~tfirstcd ( ]onstsubs=this.subs.sj u v : p 3 ^lice()if(process.env.NODE_ENV!=='production'&&!config.async){//su[ o 0bsaren'tsortedinschedulerifnotrunningasync//weneedtosortthemnowtom2 9 ] 8 e ;akesuretheyfireincorrect//ordersubs.sort((a,b` f h)=>a.id-b.id)}for(leti=0,l=subs.length;i<l;i++){subs[i].updu c . & (ate()//依靠中的update办法}}
-
第二步其实便是在第一步的 noA ^ m j N I 8 ttify
办法中,遍历subs
,履行subs[i].update()
办法,也便是顺次调用watcher
的update
办法。对应源码src/core/observer/watcher.js
的 16j e e m 4 P C A4 行
/***Subscriberinterface.*( N ) _ k } m b WWillbecalledwhenad_ * a C a 7ependencychanges.*/update(){/*istanbulignoreelse*/if(this.lazy){//核算特点this.dirty=true}elseif(thiw e 5 M ( r .s.sync){//同步watcherthis.run()}else{queueWK & p ;atcher(this)//当数据发生改变时会将watcher放到一个行列中批量更新}}
-
第三步是履行 update
函数中的queu@ E W h e ` = % -eWatcher
办法。对应d # 4 C ] S w源码& 7 2 8 F s ] ( psrc/core/obser` Q 4 #ver/scheduler.js
的 164 行。
/***Pushawatches i ( ? ( / Zrintothewatcherqueue.*JobswithduplicateIDswillbeskippedunlessit's*pushq t Me% 5 u ldwhenthequeueisbeingflushed.*/exportfu+ 6 9nctil A b ) d j `onO , P YqueueWatcher(watcher:Watcher){constid=watcher.id//过滤watcher,多个特点可能会依靠同一个waN s (tcherif(has[id]==null){has[id]=trueif(!flushing){queue.puY v % s / 3sh(, C # [watcher)//将watcher放到行列中}else{//ifalreadyflushing,splicethewatcv Z 4 N Gherbasedonitsid//ifalreadypastitsid,itwillberunnextimmediately.leti=queue.lee x P Cngt6 ` 8 + F bh-1while(i>index&&queue[i].id>watcherz g f.id){i--}queue.spl: & Yice(i+1,0,^ 8 E % G 7 $watG x I B } W R P gcher)}//queuetheflushif(!waiting)W G R ~ $ I{waiting=trueif(process.env.NOx l ~ [ Y n EDE_ENV!=='production'X P $ 8 5 T g&av K 9 ? N G X zmp;&!config.async){flushSchedulerQueue()return}nextTick(flushSchedulerQueue)//调用nextTick办法,鄙人一个tick中改写watcher行列}}}
-
第四步便是履行 nf o H | i v e U ;extTick(flushSchedulerQueue)
办法,鄙人一个tick
中改写watcher
行列
谈一下nextTick
的完成原理?
Vue.js
在默许状况下,每次触发某个数据的 setter
办法后,对应的 Watcher
目标其实会被 push
进一M E ` ?个行列 queue
中,鄙人一个 tick
的时分将这个行列 queue
悉数拿出来 run
( WaB * A Y $ Mtcher
目标的一个办法,用来触发 patch
操作) 一遍。
由于现在浏览器渠道并没有b + [ _完成 nextTick
办法,所以 Vue.js
源码平别离用 Promise
、setTi7 I tmeout
、setImmediate
等方式在 microtask
(或是tas# ^ 8 o G | Pk
)中创立一个事情,目的是在当时调用栈履行完毕以后(不一定立即)才L 0 : c u M G , t会去履行这个事情。
nextTick
办法主要是使用了宏使命和微使命,界说了一个异步办法.多次调用nextTick
会将办法存入行列中,经过这个异步办法清空当时行列。
❝
所以这个
nextTick
办法是异步办法。❞
-
首要会调用 nextTick
并传入cb
。对应源码src/core/util/next-tick.# 2 * ; z E e | Djs
的 87 行。
exportfunctionnextTick(cb?:Fun6 & $ ! t = E (ction,ctx?:Object){let_resolvecallbacks.push(()=>{if(cb){try{_ 5 F s #cb.call(ctx)}cF X V # 9 a eatcA 0 X ` e .h(e){haR } k v P N 2 EndleError(e,ctx,'neq r e 3 W J =xtTick')}}elseif(_resolve){_reso2 E . ; 0 u C 1 qlve(ctx)}})if(!pending){pending=truet8 u W W E *imerFunc()}z ` p r C } v//$flow-disable-lineif(!cb&&typeofP+ H r 4romise!=='undefined'){returnnewPromise(resolve=>{_resolve=resolve})}}
-
接下来会T i M $ s v 2 7界说一个 callbacks
数组用来存储nextTick
,鄙人一个t , . : Jick
处理这些q 0 V 6 c f回调函数之前,一切的cb
都会被存在这个callbacks
数组中。 -
下一步会调用 t` n % IimerFunc
函数。对应源码src/core/util/next-tick.js
的 33 行。
lettimerFuncif(typeoF K - K nfPromise!=| 7 8 s O 2 L U a='undefined'&&isNative(Promise)){timerFunc=()=>{//...u ; N !}isUsingMicroTask=true}elseif(!isIE&&typeofM0 6 mutationObY T Lserver!=='undefined'&&(isNative(MutationObserver)||//PhantomJSandiOS7.xMutationObserver.toString()==='[objectt | = AMutationObserverConstructor]')){tiC c ? O imerFunc=()=>{//...}isUsingMicroTask=true}elseif(typeofsetImmediate!=='undefined'&&isNative(setImmediate)){timerFunc=()=&g+ ` X t;{setImmediate(flushCallbacks)}}else{//Fallb& , U L E 6 3acktosetTi _ q / X eimeout.timerFunc=()=>{setTi* 5 j G %meout(flushCallbacke T i `s,0)}}
来看下time| g 1 1rFunc
的取W B r值逻辑:
1、 咱们知道异步使命有两种,其间 microtask
要优于 macrotask
,所以优先选择 Promise
。因此这儿先判别浏览器是否支撑 Promise
。
2、 假如不支撑再考虑 macrotask
。对于 macrotask
会先后判别浏览5 ^ . H $ m |器是否支撑 MutationObserver
和 setImmediate
。
3、 假如都不支撑就只能使用 setTimeout
。这也从侧面展现出了 macrotask
中 setTimeout
的功能是最差的。
❝
nextTick
中if (!pending)
句子中pending
效果显然是让if
句子的逻辑只履行一次,而它其实就代表P u 9 z F Xcallbacks
中是否有事情在等候履行。❞
这儿的flushCallbacks
函数的主要逻辑便是将 pending
置为 false
以及清空 callbacks
数组,然后遍历 callbacks
数组,履行里边的每一个函数。
-
nextTik G H Z 5 ?ck
的终究一步对应:
if(!cb&, N ? k ) Kamp;&typeofPromise!=='undefined')e N 8 0 5 :{returnnewPromise(resolve=>{_resolveh @ m - D J=resolve})}
这儿 if
对应的状况是咱们调用 nextTick
函数时没有传入回调函数而且浏览器支撑 PromiI s Z U W ( 3 :se
,那么就会回来一个 Promise
实例,而且f 3 q 7 /将 resolve
赋值给 _resolve
。回到nextTiU V % p 5 a # Fck
最初的一段代码:
let_resolvecallbacks.push(()=>{if(cb){try{cb.call(ctx)}catch(e){handleEr8 A ]ror(e,ctx,'nextTi- P :ck')}}elseif(_resolve){_resolve(ctx)}})
当咱们履行 callbacks
的函数时,发现没有 cb
而有 _resolve
时就会履行之前回来的 PG + I . ( &romise
目标的 resolve
函数。
你v | b | 3 _ ^ M知道Vue
中computed
是怎样完G n s : 成的吗?
这儿先给r } K e d ^ r一个定论:核算特点computed
的实质是 computed Watcher
,其具有缓存。
一张图了解下computed
的完成:

-
首Y P W K 1 i 9 p要是在组件实例化时会履行 initComputed
办法。对应源码src/core) ! q `/instance/statI ? z 4 v C j M ae.js
的 169 行。
constcompuD c i F RtedWatcherOptions={lazy:true}functioninitComputed(vm:Component,compub u l R hted:Object){//$flow-disable-lineconstwatcD ~ Phers=vm._computedWatchers=Object.create(null)//computedpropertiesarejustgettersduringSSRconstisSSR=isServerRendering()for(constkeyt * 0 = A z o Winc. q ; X / Nomputed){constuserDef=computed[key]constgetter=typeofuserDef==='function'?userDef:userDef.getif(process.env.NODE_ENV!=='production'&&getter==null){warn(`Getterismissi+ V lngforcomputedproperty"${key}".`,vm)}if(!isSSR){//createinternalwatcherforthecomputedpro+ l f Gperty.watchers[key]=newWatcher(vm,getter||noop,noop,comput; d ` M %edWatcherOptions)}//component-definedcomputedpropertiesarealreadydefinedonthe//componentprototype.Weonlynee_ : R Z P b Qdtodefinecomputedpropertiesdefined//atinstantiationhere.if(!(keyinvm)){defineComputed(vm,kr j Jey,userDef)}elseif(procesR [ f l 2 * b Js.env.NODE_ENV!=='pk 9 Yroduction'){if(keyinvm.$data){warn(`Thecomputedproperty"${key}"isalreadydefinedindata.`,vm)}eQ J + x q w Jlseif(vm.$options.props&&keyinvm.$options.x P a & E ?props){warn(`Thecomputp 7 x w b 8 q H (edproperty"${key}"isan s r F = { Slreadydefinedasaprop.`,vm)}}n 7 4 @ } J}}
init7 G Q , )Computed
函数拿到 computed
目标然后遍历每一个核算特点。判别假如不是W N H f p服务端烘托就会给核算特点创立一个 computed Watcher
实例赋值给watchers[key]
(对应便是vm._computedWatchers[key]
)。然后遍历每一个核算特点调用 defineCompup o fted
办法,U / _ 4 ~ u L G将组件原型,核算特点和对应的值传入。
-
de] 4 rfinG v A : w UeComputed
界说在源码src/core/instance/4 3 O {state.js
210 行。
//src/core/instance/sZ W ^ 2 Otate.jseb I ]xportfunctiondefineComputed(target:any,key:stringU ) 5,userDef:Object|Function)1 r q ] ?{constshouldCac( G o I & @he=!isServerRendering();if(typeofuserDef==="function"){sharedPropertyDefinition.get=shouldCache?createComputedGetter(key):createGetterInvoker(userDef);sharedPropertyDefinition.set=noop;}else{sharedPropertyDefinition.get=u( I l i J 0serDef.get?shouldCache&&userDef.cache!==false?createComputedGetter(key):createD X - uGetterInvoker(userDef.get):noop;shared) z ePropertyDefinition.set=uses 3 j 0 T W grDef.set||noop;}if(process.env.NODE_ENV!=="production"&&sharedPropertyDefinition.set===noop){m l ksharedPropertyDefinition.set=function(){warn(`Computedproperty"${key}"wasa: 3 v l P q v &ssignedtobutithasnosetter.`,this);};}Object.defineProperty(target,key,sharedPropertyDefinition);}a t # a u 5
首要界说了 shouldR s rCache
表明是否需要缓存值。接着对 userDef
是函数或许目标别离处理。这儿有一个 sharedPropertyDefinition
,咱们来看它的界说:
//src/core/instance` Q 3 S/state.jsconstsharedPropertyDefinition=( S : R | r 9 g /{enumT o R K @ H Perable:true,configurable:true,get:noop,set:noop,};
sharedPropertyDeq K jfinition
其实便是一个特点描述符。
回到 defineComputed
函数。假如 uV s e 0 Y ^ . C SserDef
是函数的话,就会界说 getter
为调用 createComputedGez q I j Ptter(key3 f n , m K Z u)
的回来值。
❝
由于
shouldCache
是true
❞
而 userDef
是目标的话,非服务端烘托而且没有指定 cache
为 false% ` * | B b 4
的话,getter
也是调用 createComputedGetter(key)
的回来值,setter
则为[ ! # f userDef.set
或许为空。
所以 defin! e R D _ _eComputed
函数的效果便是界说 getter
和 setter
,而且在终究调用 Object.defineProperty
给核算特点增加 getter/setter
,当咱们拜访核算特~ 3 # Z t p点时就会触发这个 getter
。
❝
对于核算特点的Z E n l R 1 ` L i
setter
来说,实际上是很少用到的,除非咱们在使用computed
的时分指定了set
函数。❞
-
无论是 u} z 7serDef
是函数仍是目标,终究都会调用createComputedGetter
函数,咱们来看createComputedGetteg 7 8 j jr
的界说:
functioncreateCI M .omputedGetter(key){reA O S 0 . y Q YturnfuR } G [nctioncomputedGetter(){constwatcher=this._computedWatchers&&this._computedWag 1 ztchers[key];if(watcher){if(watcher& g X.dm i m v Firty){watcher.evaluate();}if(Dep.target){watcher.depS f ~end();}returnwatcher.value;}};}
❝
咱们知道拜访核算特点时才会触发这个
getter
,对应便是computedGetter
函数被履行。❞
computedGetter
函数首要经过 thir _ Bs._computedWatchers[key]
拿到前面实例化组件时创立的 computed Watcher
并赋值给 watcher
。
❝
在
n[ : a ^ ~ X ? 9 .ew Watcher
时传入的第四个参数computedWatcherOptions
的lazy
为true
,对应便是watcher
的构造函数中的dib % r [ S crty
为true
。在computedGetter
中J ? E 1 l F t q,假如dz s 9 ] i wirty
为true
(即依靠的值没有发生改变),就不会从头求值。相当于computed
被缓存了。❞
接着有两个 if
判p h B S $别,首要调用 evaluate
函数:
/***W # l n p l tEvaluatethevalueofthewatcher.*Thisonlygetscalledforlazywatchers.*/evaluate(){this.value=this.get()this.dirty=false}
首要调用 this.get()
将它的回来值赋值给 this.value
,来看 get
函数:
//src/core/observer/watcher.js/& . l***Evaluatethegetter,andre-collectdependencies.*/get(){pushTarget(this)letvalueconstvm=this.vmtry{value=this.getter.call(vm,vm)}catch(e){if(this.user){handleError(e,vm,`getterforwatcher"${this.expression}"`)}else{throwe}}finally{//"touch"everypropertysotheyarealltrackedas//dependenciesfordeepwatchingif(this.deep){traverse(value)}popTarget()this.cleanupDeps()}returnvalue}
get
函数第一步是调用 pushTarget
将 computed Watcher
传入:
//src/core/observer/dep.jsexportfunctionpushTac d v l S rget(target:?Watcher){targetStack.push(target);Dep.target=targey c C 3 n i ! et;}
能够看到 computed Watcher
被 push 到 targetStack
一起将 Dep^ Q W 3 ~.target
置为 computed WU ` P # Datcher
。而y h W { 3 Dep.target
原来的值是烘托 Watcher
,由于正处于烘托阶段。回到 get
函数,接着就调用了 s [ | % this.getter
。
回到 evaluate
函数:
evaluate(){this.value=this.get()this.dirty=fals2 D N V # qe}
履行完get
函数,将dirty
置为false
。
回到computedGetter
函数,接着往下进入另一个if
判别x 6 ; &,履行了depend
函U ) = S A数:
//src/core/observer/watcher.js/***Dependonalldepscolled 8 T k l Cctedbythiswatcher.*/depend(){leti=y g 8 pthis.deps.lengthwhile(i--){thiN 8 s vs.deps[i].depend()}}
这儿的逻辑便是G Z . : ,让 Dep.tarw Z e s Q D t Wget
也便是烘托 Watcher
订阅了 thi3 $ | r d f X +s.dep
也便是前面实例化 compuP U j ] p } ( Hted Wae @ f xtcher
时分创立的 dep
实例,烘托 Watcher
就被保存到 this.dep
的 subs
中。
在履行P & 4 C q 0完 evaluate
和 depend
函数后,computedGetter
函数终究将 evaluate
的回来值回来出去,也便是核算特点终究核算出来的值,这样页面就烘托出来了。
评论(0)