摘要:場(chǎng)景為了多維度掌控嫌疑犯的犯罪特征數(shù)據(jù),你警署最高長(zhǎng)官想要獲取并實(shí)時(shí)監(jiān)控張三的貸款數(shù)額存貸比存款和貸款兩者比率的變化。
================前言===================
初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式講解源碼;
本系列文章:
《【用故事解讀 MobX源碼(一)】 autorun》
《【用故事解讀 MobX源碼(二)】 computed》
《【用故事解讀 MobX源碼(三)】 shouldCompute》
《【用故事解讀 MobX 源碼(四)】裝飾器 和 Enhancer》
《【用故事解讀 MobX 源碼(五)】 Observable》
文章編排:每篇文章分成兩大段,第一大段以簡(jiǎn)單的偵探系列故事的形式講解(所涉及人物、場(chǎng)景都以 MobX 中的概念為原型創(chuàng)建),第二大段則是相對(duì)于的源碼講解。
本文基于 MobX 4 源碼講解
=======================================
在寫(xiě)本文的時(shí)候,由于 MobX 以及升級(jí)到 4.x,API 有較大的變化,因此后續(xù)的文章默認(rèn)都將基于 4.x 以上版本進(jìn)行源碼閱讀。
前一篇文章仍然以 mobx v3.5.1 的源碼,autorun 邏輯在新版中沒(méi)有更改,因此源碼邏輯仍舊一致。
A. Story Time 1、 場(chǎng)景為了多維度掌控嫌疑犯的犯罪特征數(shù)據(jù),你(警署最高長(zhǎng)官)想要獲取并實(shí)時(shí)監(jiān)控張三的 貸款數(shù)額、存貸比(存款和貸款兩者比率) 的變化。
于是你就擬定了新的命令給執(zhí)行官 MobX:
var bankUser = mobx.observable({ income: 3, debit: 2 }); var divisor = mobx.computed(() => { return bankUser.income / bankUser.debit; }); mobx.autorun(() => { console.log("張三的貸款:", bankUser.debit, ";張三的存貸比: " + divisor); });
相比上一次的命令,除了監(jiān)控張三貸款這項(xiàng)直接的指標(biāo),還需要監(jiān)控 貸款比(divisor) 這項(xiàng)間接指標(biāo)。
執(zhí)行官 MobX 稍作思忖,要完成這個(gè)任務(wù)比之前的要難一點(diǎn)點(diǎn),需要費(fèi)一點(diǎn)兒精力。
不過(guò),這也難不倒能力強(qiáng)大的 MobX 執(zhí)行官,一番策略調(diào)整之后,重新拿出新的執(zhí)行方案。部署實(shí)施之后,當(dāng)張三去銀行存款、貸款后,這些變化都實(shí)時(shí)反饋出來(lái)了:
2、部署方案這次的部署和前一次相差不大,除了需要讓觀察員 O2(監(jiān)視 income)參與進(jìn)來(lái)之外,考慮到警署最高長(zhǎng)官所需的 存貸比 (divisor),還得派出另一類職員 —— 會(huì)計(jì)師:
會(huì)計(jì)師:此類職員專門(mén)負(fù)責(zé)計(jì)算,從事 數(shù)據(jù)的再加工(此項(xiàng)任務(wù)中,就是搜集數(shù)據(jù)并計(jì)算 存貸比)
會(huì)計(jì)師是一個(gè)很有意思的角色,要想理解他們,必須得思考他們的數(shù)據(jù)“從哪兒來(lái)?到哪里去?” 這兩個(gè)問(wèn)題:
從哪兒來(lái):從觀察員那兒獲取,也可以從其他會(huì)計(jì)師那兒獲取;
到哪兒去:所生產(chǎn)的數(shù)據(jù),要么是被探長(zhǎng)消費(fèi),要么被其他會(huì)計(jì)師所用;(當(dāng)然,沒(méi)有人消費(fèi)他所生產(chǎn)的數(shù)據(jù)也是可能的,不過(guò)這就得追究 MobX 執(zhí)行官的責(zé)任了,浪費(fèi)了人力資源)
引入了會(huì)計(jì)師角色之后,MobX 執(zhí)行官重新繪制了部署計(jì)劃圖:
解釋一下此計(jì)劃圖的意思:
明確此次任務(wù)是 當(dāng)張三賬戶存款或者貸款變更時(shí),打印其貸款數(shù)額(debit)和存貸比(divisor):
() => { console.log("張三的貸款:", bankUser.debit, ";張三的存貸比: " + divisor); }
將任務(wù)指派給執(zhí)行組中的探長(zhǎng) R1
派遣 2 名觀察組中的觀察員 O1、O2 分別監(jiān)察張三賬戶的 bankUser.income 屬性和 bankUser.debit 屬性;
派遣計(jì)算組中的會(huì)計(jì)師 C1 計(jì)算張三的貸款比,其所需數(shù)值來(lái)源于觀察員 O1、O2;
探長(zhǎng) R1 任務(wù)中所需的“張三的賬戶存款” 數(shù)值從觀察員 O2 那兒獲取;所需的 “張三的存貸比” 數(shù)值從會(huì)計(jì)師 C1 那兒獲??;
同時(shí)架設(shè)數(shù)據(jù)情報(bào)室,方便信息交換;
2.1、部署細(xì)節(jié)因?yàn)檫€是 autorun 命令,所以仍然執(zhí)行 A計(jì)劃方案(詳情參考上一篇《【用故事解讀 MobX源碼(一)】 autorun》)MobX 執(zhí)行官的部署方案從整體上看是一樣的,考慮到多了會(huì)計(jì)師這個(gè)角色的參與,所以特意在探長(zhǎng) 獲取存貸比(divisor) 邏輯處空出一部分留給會(huì)計(jì)師讓它自由發(fā)揮:
這樣做,MobX 執(zhí)行官也為了在實(shí)際行動(dòng)中向他的警署長(zhǎng)官證實(shí)該 A計(jì)劃方案 的確擁有“良好的擴(kuò)展性”。
解開(kāi)這層新增的會(huì)計(jì)師計(jì)算邏輯 “面紗”,圖示如下:
你會(huì)發(fā)現(xiàn)歷史總是驚人的相似,新增的會(huì)計(jì)師執(zhí)行計(jì)算任務(wù)的邏輯其實(shí) 探長(zhǎng) 執(zhí)行任務(wù)的邏輯是一樣的,下圖中我特意用 相同的序號(hào)(不同的顏色形狀)標(biāo)示 出,序號(hào)所對(duì)應(yīng)含義如下:
設(shè)置成 正在執(zhí)勤人員
開(kāi)始執(zhí)行任務(wù)
從觀察員或會(huì)計(jì)師那兒獲取執(zhí)行任務(wù)所需的數(shù)值,并同他們?nèi)〉寐?lián)系,
計(jì)算任務(wù)執(zhí)行完成后,更新與觀察員 O1、觀察員 O2 之間的聯(lián)系;
此執(zhí)行計(jì)算任務(wù)的邏輯,如果不告訴觀察員的話,觀察員還以為又來(lái)了一名“探長(zhǎng)”上級(jí)。?
從部署圖里我們可以看出會(huì)計(jì)師具有兩面性;
對(duì)探長(zhǎng)而言:會(huì)計(jì)師和觀察員地位差不多,都屬于“下級(jí)”,都需要將自己的信息及時(shí)反饋給探長(zhǎng);
對(duì)觀察員而言:會(huì)計(jì)師是屬于 “上級(jí)”,擁有部分類似探長(zhǎng)執(zhí)行任務(wù)權(quán)力,只不過(guò)其任務(wù)類型只能是 計(jì)算類型的任務(wù),執(zhí)行任務(wù)結(jié)束之后,像探長(zhǎng)那樣和觀察員互相關(guān)聯(lián)起來(lái),方便下一次的運(yùn)算;
自從有了會(huì)計(jì)師的參與,探長(zhǎng)還是那個(gè)探長(zhǎng),但他的下級(jí)已經(jīng)不是之前的下級(jí)了。借助 A計(jì)劃任務(wù)的執(zhí)行,會(huì)計(jì)師 C1 在上報(bào)計(jì)算值的時(shí)候,會(huì)順?biāo)浦鄣貓?zhí)行計(jì)算任務(wù),同時(shí)更新他的 ”關(guān)系網(wǎng)“。
2.2、 懶惰的會(huì)計(jì)師會(huì)計(jì)師有一個(gè)特性就是比較懶:就算觀察員所觀察到的值變更了,他們也不會(huì)立即重新計(jì)算,而只在必要的時(shí)候(比如當(dāng)上級(jí)前來(lái)索取時(shí))才會(huì)重新計(jì)算。
舉個(gè)例子,當(dāng)觀察員 O1 發(fā)現(xiàn)張三的賬戶存款從原來(lái)的 3 變成 6 :
bankUser.income = 6;
這個(gè)時(shí)候會(huì)觸發(fā)一系列的 “漣漪”:
① 觀察員 O1 先注冊(cè)事務(wù),相當(dāng)于到數(shù)據(jù)情報(bào)室”上班打卡“,聲明這次事件由 觀察員 O1 主導(dǎo)
② 告知其上級(jí),也就是會(huì)計(jì)師 C1 ,說(shuō)是張三存款(income)有變更
③ 會(huì)計(jì)師 C1 獲知消息后,”慵懶地“調(diào)整自己的狀態(tài)
④ 隨后會(huì)計(jì)師 C1 繼續(xù)往上級(jí)匯報(bào),告知本會(huì)計(jì)師的值有更改(注意,此時(shí)會(huì)計(jì)師只是告訴上級(jí)自己的值有更改這一事實(shí),但并沒(méi)有執(zhí)行計(jì)算任務(wù) ?。?/p>
⑤ 探長(zhǎng) R1 接收到會(huì)計(jì)師的反饋后,就向 MobX 執(zhí)行官申請(qǐng)要執(zhí)行任務(wù)!因?yàn)槠湎录?jí)會(huì)計(jì)師 C1 匯報(bào)說(shuō)值有更改,說(shuō)明這個(gè)時(shí)候應(yīng)該要重新執(zhí)行任務(wù)啦~
⑥ 執(zhí)行官 MobX 調(diào)閱數(shù)據(jù)情報(bào)室信息一看,發(fā)現(xiàn)目前觀察員 O1 正在執(zhí)行事務(wù),就讓探長(zhǎng) R1 再等等,現(xiàn)在不是執(zhí)行任務(wù)的最佳時(shí)機(jī),等到事務(wù)結(jié)束再說(shuō)。
⑦ 不一會(huì)兒觀察員 O1 完成了自己的職責(zé),”下班打卡“,在數(shù)據(jù)情報(bào)室中注銷(xiāo)事務(wù)
⑧ 這個(gè)時(shí)候,執(zhí)行官 MobX 才讓探長(zhǎng) R1 開(kāi)始執(zhí)行任務(wù)
將上面的文字轉(zhuǎn)換成流程圖,可以清晰看到各角色在這次“漣漪”中所起到的作用:
這里需要注意 3 點(diǎn):
當(dāng)觀察員O1 匯報(bào)張三存款有更改的時(shí)候,會(huì)計(jì)師 C1 并沒(méi)有立即重新計(jì)算值哦,僅僅是更改自身的狀態(tài);
會(huì)計(jì)師告知上級(jí)(探長(zhǎng) R1)自己有值更改,探長(zhǎng)申請(qǐng)執(zhí)行任務(wù),不過(guò) MobX 執(zhí)行官并沒(méi)有允許他這么做,而是讓他先等待一下,因?yàn)榇藭r(shí) 觀察員 O1 還在匯報(bào)工作。等觀察員 O1 工作匯報(bào)完畢,這個(gè)時(shí)候才讓探長(zhǎng)執(zhí)行任務(wù)。因?yàn)橛锌赡苡衅渌?jì)算組職員也正在響應(yīng)該觀察值的更改,事情一件一件來(lái),不要著急,這和 debounce 思想一致,減少不必要的計(jì)算。
只有在最后探長(zhǎng)執(zhí)行任務(wù)時(shí) 需要用到會(huì)計(jì)師的值的時(shí)候,會(huì)計(jì)師才會(huì)去執(zhí)行計(jì)算操作。這就是典型的惰性求值思維。
會(huì)計(jì)師這種拖延到 只有被需要的時(shí)候才進(jìn)行計(jì)算 的行為,有沒(méi)有讓你回憶起學(xué)生時(shí)代寒假結(jié)束前一天瘋狂補(bǔ)作業(yè)的場(chǎng)景??
2.3、避免不必要的計(jì)算當(dāng)執(zhí)行官 MobX 拿著這份執(zhí)行報(bào)告送達(dá)給你(警署最高長(zhǎng)官),閱覽完畢:”不錯(cuò),這套方案的確部分證實(shí)了你之前所言的可擴(kuò)展性。但隨著職員的引入,運(yùn)轉(zhuǎn)機(jī)構(gòu)逐漸龐大,如何避免不必要的開(kāi)銷(xiāo)的呢?“
”長(zhǎng)官您高瞻遠(yuǎn)矚,這的確是一個(gè)問(wèn)題。在井然有序的規(guī)則下,個(gè)別職員的運(yùn)作效率的確會(huì)打折扣。因此避免職員不必要的計(jì)算開(kāi)銷(xiāo),也是在我方案部署規(guī)劃之內(nèi)。正如您所見(jiàn),上述方案中會(huì)計(jì)師的‘惰性’、探員在事務(wù)之后再進(jìn)行任務(wù)等機(jī)制,都是基于優(yōu)化性能所采取的措施。“ 執(zhí)行官 MobX 稍作停頓,繼續(xù)道,”為了更好地闡述這套運(yùn)行方案的性能優(yōu)化機(jī)制,我明天呈上一份報(bào)告,好讓您得以全面了解?!?/p>
”Good Job!期待你的報(bào)告“。
那么,執(zhí)行官 MobX 是憑借什么機(jī)制減少開(kāi)銷(xiāo)的呢?且聽(tīng)下回分解。
(本節(jié)完,未完待續(xù))
本節(jié)部分,仍然是就著上面的”故事“來(lái)講 MobX 中的源碼。
先羅列本文故事中新出現(xiàn)的 會(huì)計(jì)師 角色與 MobX 源碼概念映射關(guān)系:
故事人物 | MobX 源碼 | 解釋 |
---|---|---|
會(huì)計(jì)師 | computedvalue | 官方文檔 - (@)computed 計(jì)算值 |
探長(zhǎng)、執(zhí)行官等角色的映射關(guān)系,參考上一篇《【用故事解讀 MobX源碼(一)】 autorun》
本文的重點(diǎn)內(nèi)容就是 computedvalue 的部分源碼(它在 autorun 等場(chǎng)景中的應(yīng)用)
autorun(A 計(jì)劃)的源碼在上一節(jié)講過(guò),這里不再贅述。我們僅僅講解一下 computedValue 在 autorun 中的表現(xiàn)。
1、會(huì)計(jì)師,請(qǐng)開(kāi)始你的表演在故事中我們講到過(guò),當(dāng)探長(zhǎng)向會(huì)計(jì)師索要計(jì)算值的時(shí)候,此時(shí)懶惰的會(huì)計(jì)師為了 ”應(yīng)付交差“,這時(shí)候才開(kāi)始計(jì)算,其計(jì)算的過(guò)程和探長(zhǎng)執(zhí)行的任務(wù)流程幾乎一致。
從源碼角度去看一下其中的原因。
當(dāng)探長(zhǎng)執(zhí)行任務(wù):
() => { console.log("張三的貸款:", bankUser.debit, ";張三的存貸比: " + divisor); }
任務(wù)中也涉及 bankUser.debit 變量和 divisor 變量;其中在獲取 bankUser.debit 變量之時(shí)會(huì)讓觀察員 O2 觸發(fā) reportObserved方法,這個(gè)上一篇文章著重講過(guò),此處就不詳細(xì)展開(kāi)了;而請(qǐng)求 divisor 數(shù)值的時(shí)候,則會(huì)觸發(fā)該值的 valueOf() 方法 —— 即調(diào)用會(huì)計(jì)師(computedValue)的 valueOf() 方法。
為什么調(diào)用就觸發(fā) valueOf() 方法呢?請(qǐng)看下方的“知識(shí)點(diǎn)”備注?
======== 插播知識(shí)點(diǎn) =========任何原始值還是對(duì)象其實(shí)都包含 valueOf() 或 toString() 方法,valueOf() 會(huì)返回最適合該對(duì)象類型的原始值,toString() 將該對(duì)象的原始值以字符串形式返回。
這兩個(gè)方法一般是交由 JS 去隱式調(diào)用,以滿足不同的運(yùn)算情況。比如在數(shù)值運(yùn)算(如a + b)里會(huì)優(yōu)先調(diào)用 valueOf(),而在字符串運(yùn)算(如alert(c))里,會(huì)優(yōu)先調(diào)用 toString() 方法
順帶附上兩篇 參考文章
js中 toString 和 valueOf 的區(qū)別?:知乎問(wèn)答
valueOf() vs. toString() in Javascript:SF 上的回答,非常詳盡地告訴你其執(zhí)行結(jié)果
======== 完畢 ==========
一旦調(diào)用調(diào)用會(huì)計(jì)師的 valueOf 方法:
valueOf(): T { return toPrimitive(this.get()) }
其實(shí)就是調(diào)用 this.get() 方法,我們瞧一眼源碼;
1.1、 重量級(jí)計(jì)算 還是 輕量級(jí) 計(jì)算?這里有個(gè)分叉點(diǎn),根據(jù) globalState.inBatch 決定到底是啟用 重量級(jí)計(jì)算 還是 輕量級(jí)計(jì)算:
當(dāng) globalState.inBatch 值大于 0,說(shuō)明會(huì)計(jì)師被上級(jí)征調(diào)(處于上級(jí)事務(wù)中),比如此案例中,陷于 A 計(jì)劃(autorun )的會(huì)計(jì)師,在上級(jí)探長(zhǎng) R1 需要查閱計(jì)算值時(shí)候,就會(huì)進(jìn)入重量級(jí)計(jì)算模式
當(dāng)會(huì)計(jì)師無(wú)上級(jí)征調(diào)的時(shí)候,globalState.inBatch 值為 0,就會(huì)進(jìn)入輕量級(jí)計(jì)算模式,簡(jiǎn)化計(jì)算的邏輯。
但無(wú)論輕量級(jí)還是重量級(jí)計(jì)算,都會(huì)涉及到調(diào)用 computeValue() 方法來(lái)執(zhí)行計(jì)算任務(wù)。
調(diào)用的時(shí)候,如果是 重量級(jí)計(jì)算 則 track 這個(gè) bool 值為 true,否則track 值為 false。
計(jì)算值有個(gè)屬性,this.derivation 就是會(huì)計(jì)師要計(jì)算數(shù)值時(shí)所依據(jù)的計(jì)算表達(dá)式,也就是而我們定義會(huì)計(jì)師時(shí)所傳入的匿名函數(shù):
() => { return bankUser.income / bankUser.debit; }
無(wú)論是 重量級(jí)計(jì)算 模式還是 輕量級(jí)計(jì)算 模式,最終都是會(huì)調(diào)用該計(jì)算表達(dá)式獲取計(jì)算值。
重量級(jí)計(jì)算 模式和 輕量級(jí)計(jì)算 模式兩者的差別只是在于前者在執(zhí)行該計(jì)算表達(dá)式之前會(huì)設(shè)置很多環(huán)境,后者直接就按這個(gè)表達(dá)式計(jì)算數(shù)值返回。
在上述的故事中,由于探長(zhǎng) R1 人物的存在,會(huì)計(jì)師會(huì)執(zhí)行 重量級(jí)計(jì)算 模式,接下來(lái)的源碼分析也走這條分支路線。( 輕量級(jí)計(jì)算 模式的情況當(dāng)做課后思考題)。
1.2、像探長(zhǎng)學(xué)習(xí)在 重量級(jí)計(jì)算的時(shí)候,computeValue(true) 就會(huì)走和 探長(zhǎng) 操作模式一樣 trackDerivedFunction 步驟。沒(méi)錯(cuò),探長(zhǎng)和會(huì)計(jì)師調(diào)用的就是同一個(gè)方法,所以他們?cè)趫?zhí)行任務(wù)的時(shí)候,行為痕跡是一樣的,沒(méi)毛病。
如果忘記 trackDerivedFunction 方法內(nèi)容,請(qǐng)查看 《【用故事解讀 MobX源碼(一)】 autorun》的 ”2.2.2、trackDerivedFunction“ 部分
只不過(guò)會(huì)計(jì)師只能執(zhí)行計(jì)算類的任務(wù)(純函數(shù))罷了,探長(zhǎng)可以執(zhí)行任意類型的任務(wù)。
和探長(zhǎng)一樣,會(huì)計(jì)師執(zhí)行計(jì)算任務(wù)完畢之后調(diào)用 bindDependencies 將綁定 觀察員 O1 和 觀察員 O2 ;而在執(zhí)行計(jì)算之后,會(huì)計(jì)師會(huì)調(diào)用 propagateChangeConfirmed 方法,更改自己和上級(jí) 探長(zhǎng) 的狀態(tài) —— 這說(shuō)明,對(duì)探長(zhǎng)而言,會(huì)計(jì)師就相當(dāng)于 觀察員的角色,在探長(zhǎng)執(zhí)行任務(wù)結(jié)束后像觀察員一樣需要上報(bào)自己的計(jì)算值,并和 探長(zhǎng) 取得聯(lián)系;
這么看會(huì)計(jì)師還真 ”墻頭草,兩邊倒”。
至此,會(huì)計(jì)師這個(gè)角色以較低的成本就能完美地整合進(jìn)執(zhí)行官 MobX 所部署的 A 集合部署方案中。??
2、 響應(yīng)觀察值的變化一旦張三的賬戶存款(income)發(fā)生變化,將會(huì)觸發(fā) MobX 所提供的 reportChanged 方法:
public reportChanged() { startBatch() propagateChanged(this) endBatch() }
注意這里的 startBatch 和 endBatch 方法,說(shuō)明觀察員 O1 發(fā)起事務(wù)了。2.1、傳遞變化的信息
我們知道(不知道的請(qǐng)閱讀上一篇文章)該 reportChanged() 方法中的 propagateChanged() 會(huì)觸發(fā)上級(jí)的 onBecomeStale() 方法。
觀察員 O1 此時(shí)的上級(jí)是 會(huì)計(jì)師 C1,其所定義的 onBecomeStale 如下:
onBecomeStale() { propagateMaybeChanged(this) }
看一下 propagateMaybeChanged(this) 源碼,也比較簡(jiǎn)單,主要做了兩件事情,① 會(huì)計(jì)師會(huì)調(diào)整自身的狀態(tài); ②然后觸發(fā)其上級(jí)(探長(zhǎng) R1)的 onBecomeStale() 方法。
可見(jiàn)觀察員 01 會(huì)引起會(huì)計(jì)師 C1 的響應(yīng),而會(huì)計(jì)師會(huì)引起探長(zhǎng) R1 的響應(yīng),這種響應(yīng)“漣漪”就是通過(guò)下級(jí)觸發(fā)上級(jí)的 onBecomeStale 方法形成的連鎖反應(yīng)。
不同上級(jí)(比如會(huì)計(jì)師和探長(zhǎng))的 onBecomeStale 定義不同。
探長(zhǎng)的這個(gè) onBecomeStale 方法在上一篇文章的 “3、響應(yīng)觀察值的變化 - propagateChanged” 中我們講過(guò),探長(zhǎng)將請(qǐng)求 MobX 請(qǐng)求重新執(zhí)行一遍 A 計(jì)劃方案。
然而,MobX 拒絕了這次請(qǐng)求,讓他再等待一下。??
這是因?yàn)樵?runReactions 方法中:
if (globalState.inBatch > 0 || globalState.isRunningReactions) return
由于此時(shí) inBatch 是 1(因?yàn)橛^察員執(zhí)行了 startBatch()),所以會(huì)直接 return 掉。
直到觀察員執(zhí)行 endBatch() 的時(shí)候,除了會(huì)結(jié)束本次的上報(bào)事務(wù),同時(shí)執(zhí)行官 MobX 會(huì)重新執(zhí)行 runReactions 方法,讓久等的探長(zhǎng)去執(zhí)行任務(wù):
探長(zhǎng)在執(zhí)行任務(wù)的時(shí)候,就會(huì)打印張三的貸款(debit)、存貸比(divisor)了。
2.2、雖然懶,但是懶得有技巧綜上,當(dāng)張三存款(income)變更,就能讓 A 計(jì)劃(autorun)自動(dòng)運(yùn)行,探長(zhǎng)會(huì)打印張三的貸款(debit)、存貸比(divisor)。
這里需要提及一下,關(guān)于會(huì)計(jì)師重新計(jì)算的時(shí)機(jī),是在探長(zhǎng)執(zhí)行 shouldCompute 的時(shí)候,探長(zhǎng)發(fā)現(xiàn)會(huì)計(jì)師值 陳舊 了,就讓會(huì)計(jì)師重新計(jì)算:
看看這里,對(duì)計(jì)算值而言,isComputedValue()(如果是計(jì)算值)返回 true,就會(huì)執(zhí)行 obj.get() 方法,這個(gè)方法剛才剛講過(guò),會(huì)讓會(huì)計(jì)師執(zhí)行 重量型計(jì)算操作,更新自己的計(jì)算值。
所以,這次計(jì)算時(shí)機(jī)并非等到探長(zhǎng)執(zhí)行任務(wù)時(shí)(真正用到該值)的時(shí)候才讓其重新計(jì)算,和第一次 autorun 的時(shí)機(jī)不一致。
估計(jì)這是 MobX 考慮到會(huì)計(jì)師的值肯定需要更新的(已經(jīng)確定要被探長(zhǎng) R1 用到),還有可能會(huì)被其他上級(jí)引用,既然遲早要更新的,那就盡可能將更新前置,這樣在整體上能降低成本。
更新完之后,在探長(zhǎng)執(zhí)行任務(wù)的時(shí)候,會(huì)計(jì)師匯報(bào)自己是最新的值了,就不用再重新計(jì)算一遍。
雖然懶,但是懶得有技巧。
至此,有關(guān)會(huì)計(jì)師的源碼解讀已經(jīng)差不多,后續(xù)有想到的再補(bǔ)充。
3、其他說(shuō)明本文為了方便說(shuō)明,所以多帶帶使用 mobx.computed 方法定義計(jì)算值,平時(shí)使用中更多則是直接應(yīng)用在 對(duì)象中屬性 上,使用 get 語(yǔ)法:
var bankUser = mobx.observable({ income: 3, debit: 2, get divisor() { return this.income / this.debit; } });
這僅僅是寫(xiě)法上不一樣,源碼分析的思路是一致的。
4、小測(cè)試 4.1、測(cè)試1問(wèn)題:當(dāng)我們更改張三貸款數(shù)額 bankUser.debit = 4; 時(shí),請(qǐng)從源碼角度解答 MobX 的執(zhí)行流程是如何的?
參考答案提示:
reportChanged() => propagateChanged() => propagateMaybeChanged() => runReaction() => track() => get() => computeValue() => bindDependencies()4.2、測(cè)試2
問(wèn)題:如果不存在 autorun (即沒(méi)有探長(zhǎng)參與,僅有觀察員和會(huì)計(jì)師),此時(shí)僅改變張三存款數(shù)值:
var bankUser = mobx.observable({ income: 3, debit: 2 }); var divisor = mobx.computed(() => { return bankUser.income / bankUser.debit; }); bankUser.income = 6; // 請(qǐng)問(wèn)此時(shí)的執(zhí)行情況是什么樣的? console.log("張三的存貸比:", divisor)
請(qǐng)問(wèn)會(huì)計(jì)師會(huì)重新計(jì)算數(shù)值么?此時(shí)這套系統(tǒng)的執(zhí)行情況又會(huì)是怎么樣的呢?
參考答案提示:會(huì)計(jì)師此時(shí)執(zhí)行 輕量級(jí)計(jì)算模式。
5、小結(jié)此篇文章講解 MobX 中 計(jì)算值 (computedValue) 的概念,類比故事中的會(huì)計(jì)師角色??偨Y(jié)一下 計(jì)算值 (computedValue)的特征:
計(jì)算值是基于現(xiàn)有狀態(tài)或其他計(jì)算值衍生出的數(shù)值,一般是通過(guò) 純函數(shù) 的方式衍生而得。
一旦觀察值更改之后,計(jì)算值是能夠重新執(zhí)行計(jì)算,不過(guò)并非立即執(zhí)行,而是 惰性 的 ———— 只有在必要的時(shí)候才會(huì)執(zhí)行計(jì)算。
對(duì)觀察值而言,計(jì)算值和 autorun(或reaction) 很像,之所以相似是在 執(zhí)行任務(wù) 時(shí)都涉及到調(diào)用 trackDerivedFunction 方法;而對(duì) autorun(或reaction)而言,計(jì)算值和觀察值很相,都是數(shù)據(jù)提供者。
正如 官方文檔 而言,計(jì)算值是高度優(yōu)化過(guò)的,所以盡可能應(yīng)用他們。
下一篇文章將探討 MobX 中與 autorun 和 computed 相關(guān)的計(jì)算性能優(yōu)化的機(jī)制,看看 MobX 如何平衡復(fù)雜場(chǎng)景下?tīng)顟B(tài)管理時(shí)的效率和性能。
下面的是我的公眾號(hào)二維碼圖片,歡迎關(guān)注,及時(shí)獲取最新技術(shù)文章。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/94056.html
摘要:所以這是一篇插隊(duì)的文章,用于去理解中的裝飾器和概念。因此,該的作用就是根據(jù)入?yún)⒎祷鼐唧w的描述符。其次局部來(lái)看,裝飾器具體應(yīng)用表達(dá)式是,其函數(shù)簽名和是一模一樣。等裝飾器語(yǔ)法,是和直接使用是等效等價(jià)的。 ================前言=================== 初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式講解源碼; 本系列文章: 《【用故事解...
摘要:最簡(jiǎn)單的情況張三的存貸這里我們創(chuàng)建了實(shí)例探長(zhǎng)實(shí)例觀察員這個(gè)示例和我們之前在首篇文章用故事解讀源碼一中所用示例是一致的。 ================前言=================== 初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式講解源碼; 本系列文章: 《【用故事解讀 MobX源碼(一)】 autorun》 《【用故事解讀 MobX源碼(二)】...
摘要:隨后,執(zhí)行官給出一張當(dāng)張三存款發(fā)生變化之時(shí),此機(jī)構(gòu)的運(yùn)作時(shí)序圖的確,小機(jī)構(gòu)靠人力運(yùn)作,大機(jī)構(gòu)才靠制度運(yùn)轉(zhuǎn)。第一條語(yǔ)句創(chuàng)建觀察員第一條語(yǔ)句張三我們調(diào)用的時(shí)候,就創(chuàng)建了對(duì)象,對(duì)象的所有屬性都將被拷貝至一個(gè)克隆對(duì)象并將克隆對(duì)象轉(zhuǎn)變成可觀察的。 ================前言=================== 初衷:網(wǎng)上已有很多關(guān)于 MobX 源碼解讀的文章,但大多閱讀成本甚高。...
摘要:前言初衷以系列故事的方式展現(xiàn)源碼邏輯,盡可能以易懂的方式講解源碼本系列文章用故事解讀源碼一用故事解讀源碼二用故事解讀源碼三用故事解讀源碼四裝飾器和用故事解讀源碼五文章編排每篇文章分成兩大段,第一大段以簡(jiǎn)單的偵探系列故事的形式講解所涉及人物場(chǎng) ================前言=================== 初衷:以系列故事的方式展現(xiàn) MobX 源碼邏輯,盡可能以易懂的方式...
摘要:一其實(shí)是一個(gè)比較輕便的可擴(kuò)展的狀態(tài)管理工具,是一個(gè)由以及一些其他團(tuán)隊(duì)的人共同維護(hù)的開(kāi)源項(xiàng)目。當(dāng)應(yīng)用公共狀態(tài)的組件在狀態(tài)發(fā)生變化的時(shí)候,會(huì)自動(dòng)完成與狀態(tài)相關(guān)的所有事情,例如自動(dòng)更新自動(dòng)緩存數(shù)據(jù),自動(dòng)通知等。 一、MobX MobX其實(shí)是一個(gè)比較輕便的可擴(kuò)展的狀態(tài)管理工具,是一個(gè)由Facebook以及一些其他團(tuán)隊(duì)的人共同維護(hù)的開(kāi)源項(xiàng)目。 當(dāng)應(yīng)用公共狀態(tài)的組件在狀態(tài)發(fā)生變化的時(shí)候,會(huì)自動(dòng)完...
閱讀 2254·2021-11-22 09:34
閱讀 2025·2021-09-22 15:22
閱讀 2024·2019-08-29 15:05
閱讀 2116·2019-08-26 10:43
閱讀 3413·2019-08-26 10:26
閱讀 892·2019-08-23 18:29
閱讀 3525·2019-08-23 16:42
閱讀 2002·2019-08-23 14:46