【用故事解读 MobX源码(一)】 autorun
================前言===================
- 初衷:網上已有很多關于 MobX 源碼解讀的文章,但大多閱讀成本甚高。本人在找文章時對此深有體會,故將以系列故事的方式展現源碼邏輯,盡可能以易懂的方式講解 MobX 源碼;
-
本系列文章:
- 《【用故事解讀 MobX源碼(一)】 autorun》
- 《【用故事解讀 MobX源碼(二)】 computed》
- 《【用故事解讀 MobX源碼(三)】 shouldCompute》
- 《【用故事解讀 MobX 源碼(四)】裝飾器 和 Enhancer》
- 《【用故事解讀 MobX 源碼(五)】 Observable》
- 文章編排:每篇文章分成兩大段,第一大段以簡單的偵探系列故事的形式講解(所涉及人物、場景都以 MobX 中的概念為原型創建),第二大段則是相對于的源碼講解。
- 本文基于 MobX 3 源碼講解
=======================================
A. Story Time
1、 場景
場景:
一位名為 張三 的銀行用戶賬戶情況為:
- 賬戶存款為 3(萬元)
- 信用借貸為 2(萬元)
你作為警署最高長官,在一起金融犯罪中認定 張三 為金融犯罪嫌疑犯,想自動化跟蹤這位用戶的儲蓄情況,比如他一旦銀行存款有變更就打印出他當前的賬戶存款
為了實現這個任務,你下發命令給你的執行官(MobX):
var bankUser = mobx.observable({name: '張三',income: 3,debit: 2 });mobx.autorun(() => {console.log('張三的賬戶存款:', bankUser.income); });
執行官拿著這幾行代碼開始部署警力來完成你下發的指令,并將這次行動命名為 A計劃 (是不是很酷???)。你所要做的,就是等執行官 MobX 執行行動部署完畢之后,坐在辦公室里一邊愜意地喝著咖啡,一邊在電腦上觀察張三賬戶的存款變化。
執行官部署完畢后,首先會立即打印出 張三的賬戶存款: 3
后續張三的賬戶存款有更改的話,會 自動執行該部署方案,控制臺里就自動打印其存款;
// 更改賬戶存款 bankUser.income = 4; bankUser.income = 10;
是不是很神奇很自動化?
2、 部署方案
作為警署最高長官,你不必事必躬親過問執行官(MobX)部署的細節,只要等著要結果就可以。
而作為執行官(MobX),你得知道 A計劃 中部署方案的每一步細節。下面我們來一探究竟執行官 MobX 到底是如何部署 A計劃 的。
2.1、 組織架構
執行官(MobX) 擁有一套成熟的運作機構組織支撐任務的執行。為了執行這項任務,涉及到 2 類職員和 1 個數據情報室:
- 觀察員:其工作職責是觀察并監督嫌疑人特定信息,比如這里,監視張三的收入(income)屬性,當這項特征有變更的時候,及時向上級匯報(并執行特定的操作);
- 探長:一方面負責管理劃歸給他的 觀察員,整合觀察員反饋的資訊;另一方面接受 MobX 執行官交給他的任務,在 適當的時機 執行這項任務(此任務是打印張三的存款);
- 此外還會架設一個 數據情報室,方便執行官 MobX、探長和觀察員們 互相通過情報室進行數據信息的交換。
具體組織架構關系圖如下:
按照組織架構,執行官 MobX 分解計劃細節并安排人員如下:
1.明確此次任務是 當張三賬戶存款變更時,打印其存款:
() => {console.log('張三的賬戶存款:', bankUser.income); }2.將任務指派給執行組中的探長 R1
3.派遣觀察組中的觀察員 O1 監察張三賬戶的 bankUser.income 屬性
4.探長 R1 任務中所需的“張三的賬戶存款” 數值必須從觀察員 O1 那兒獲取;
5.同時架設數據情報室,方便信息交換;
2.2、 部署細節
人員安排完畢,執行官拿出一份 部署方案書,一聲令下 “各位就按這套方案執行任務吧!”;
在部署方案中下達之后,機構各組成員各司其職,開始有條不紊地開始運作,具體操作時序圖如下所示:
對時序圖中的關鍵點做一下解釋:
執行官 MobX 先將探長 R1 信息注冊到中心情報室;(有些情況下,比如偵破大案要案時需要多位探長協作,將存在多位探長同時待命的情況;當然此次任務中,只有探長 R1 在待命)
執行官 MobX 挨個讓每位待命探長按以下步驟操作:
- 3.1. 探長出發做任務時,記得給中心情報室通告一聲,好比上班“打卡”操作。(記錄事務序號,好讓中心情報室知曉案情復雜度;有些案件中將有很多探長同時執行任務)
- 3.2 探長 R1 開始監督并執行 MobX 交給他的任務(“打印張三的存款”)
- 3.3 首先在數據情報室中“注冊”,將自己設置成 正在執勤人員;(這一步很重要)
- 3.4 隨后真正進入執行任務的狀態
- 3.5 在執行任務的時候,發現需要張三的存款(income)這個數值,可這個數值探長 R1 不能直接獲取,必須通過觀察員 O1 取得,于是通過專用通訊機和觀察員 O1 取得聯系,請求獲取要張三的存款(income)
-
3.6 觀察員 O1 發現有人通過專用通訊機請求張三的存款(income),就開始如下操作:
- 3.6.1 將自己的信息 經過 數據情報室,然后傳達給請求方;只有上級(不一定是探長,有可能是其他的上級領導)才能通過這個專用通訊機發消息給觀察員;
- 3.6.2 數據情報室 將該信息同步給 正在執勤人員 —— 即探長 R1
- 3.6.3 同時將張三的存款(income)返回給請求方;(該消息不用經過 數據情報室)
- 3.7 此時探長擁有兩份信息:任務所需要的張三的存款(income),以及觀察員 O1 的相關信息;因時間緊,執行任務優先級高,探長 R1 先拿著張三的存款(income)數據,先做完任務。(至于觀察員 O1 的信息 先臨時保存 ,方便后續處理);
- 3.8 等到任務執行完了,探長松了一口氣,匯總并整理臨時保存的觀察員信息;在這一階段,探長 R1 才和 觀察員 O1 互相建立牢固的關系(可以理解為,互留聯系方式,可隨時聯系得上對方),也是在這個時候,觀察員 O1 才知曉其上級領導是探長 01;
- 3.9 此后,探長 R1 發消息告知中心情報室,削去此次事務(說明事務執行完了),好比下班 “打卡”操作。
上述時序圖中有些地方需要強調一下:
探長 R1 和觀察員 O1 建立關系并非一步到位,是 分兩個階段 的:
- 第一階段(對應上述步驟中 3.6.2)是在執行任務期間,僅僅是建立短暫的 單向關系;即,此時探長 R1 知曉觀察員 O1 的情況,但反過來,但觀察員 O1 并不知曉探長 R1 ;
- 第二階段(對應上述步驟中 3.8)在任務執行完,收尾階段的時候,探長才有時間梳理、整合任務期間的探員信息(因為任務中涉及到有可能多個觀察員,當然此次任務中只有 1 個),那時候才有時間 慢慢地 和各位探員互相交換信息,建立 明確且牢固 的關系;
2.3、 任務執行自動化
作為警署最高長官的你,拿著這份部署方案,眉頭緊鎖:“我說執行官 ,就為了區區獲取張三的存款這么件事兒,耗費那么多人力資源,值么?直接獲取 bankUser.income 不就行了?!”
“emm...,這所做的努力,圖的是普適性和 自動化響應?!眻绦泄?MobX 淡然自如,不緊不慢徐徐道來,“有了上面那套機制,一方面每當張三的存款變更后,就會 自動化執行上述部署方案的過程;另一方面很方便擴展,后續針對其他監察,只需要在此部署方案中稍加改動就可以,所需要的高級功能都是基于這個方案做延伸。真正做到了 ’部署一次,全自動化執行‘ 的目的。“
隨后,執行官 MobX 給出一張當張三存款發生變化之時,此機構的運作時序圖;
"的確,小機構靠人力運作,大機構才靠制度運轉。那就先試運行這份部署計劃,看它能否經受得起時間的考驗吧。" 警署最高長官拍拍執行官 MobX 的肩膀,若有所思地踱步出了辦公室。
(此節完。未完待續)
B. Source Code Time
上面講那么久的故事,是為了給講源碼做鋪墊。
接下來將會貼 MobX 源碼相關的代碼,稍顯枯燥,我只能盡量用通俗易懂的話來分析講解。
先羅列本文故事中人物與 MobX 源碼概念映射關系:
| 警署最高長官 | (無) | MobX 用戶,沒錯,就是你 |
| 執行官 MobX | MobX | 整個 MobX 運行環境 |
| A計劃 | autorun | 官方文檔 -mobx.autorun方法 |
| 探長 | reaction | 官方文檔 - Reaction 響應對象 |
| 觀察員 | observable | 官方文檔 - Observable 對象 |
| 數據情報室 | globalstate | MobX 運行環境中的 ”全局變量“,不同對象通過它進行數據傳遞通信,十分重要;(但這其實在一定程度上破壞了內聚性,給源碼閱讀、程序 debug 造成一定的難度) |
本文的重點是講解 A 計劃所對應的 autorun 的源碼,先從整體上對 MobX 的運行有個大致了解,而所涉及到的 Reaction、Observable 等細節概念后續章節再做展開,這里僅僅大致提及其部分功能和屬性;
1、下達的命令
回到故事的最開始,你給 MobX 下達的命令如下:
var bankUser = mobx.observable({name: '張三',income: 3,debit: 2 });mobx.autorun(() => {console.log('張三的賬戶存款:', bankUser.income); });只有兩條語句,第一條語句是創建觀察員,第二條語句是執行 A 計劃(內含委派探長、架設情報局等工作)
我們挨個細分講解。
1.1、第一條語句:創建觀察員 - Observable
第一條語句:
const bankUser = mobx.observable({name: '張三',income: 3,debit: 2 })我們調用 mobx.observable 的時候,就創建了 Observable 對象,對象的所有屬性都將被拷貝至一個克隆對象并將克隆對象轉變成可觀察的。
因此這一行代碼執行后, name、income 和 debit 這三個屬性都變成可觀察的;
若以故事場景來敘述中,執行官 MobX 在部署的時候委派了 3 位探員,分別監視這 3 個屬性;而故事中交給探長任務中僅僅涉及了那位監視 income 屬性的觀察員 O1;(所以另外兩位探員都還在休息)
在這里可以看到 惰性求值 的思想的應用,只有在 必要的時候 啟用 所觀察對象,粒度細,有利于性能提升;
之所以只有 1 位觀察員,是因為由于上級下達的具體任務內容是:
() => {console.log('張三的賬戶存款:', bankUser.income); }看看任務中出現 bankUser.income 而并沒有出現 bankUser.debit 和 bankUser.name,說明這個任務只 牽連 探員O1,而和其他探員無關。
注:本文暫時先不分析 mobx.observable 的源碼,留待后續專門的一章來分析;迫不及待的讀者,可以先閱讀網上其他源碼文章,比如:Mobx 源碼解讀(二) Observable觀察員有兩個非常重要的行為特征:
- 當有人請求觀察員所監控的值(比如income)時,會觸發 MobX 所提供的 reportObserved 方法;
- 當觀察員所監控的值(比如income)發生變化時,會觸發 MobX 所提供的 propagateChanged 方法;
這里留一個印象,本文后續在適當的時機再講解這兩個方法是在什么時候觸發的;
1.2、第二條語句:A 計劃的實施 - autorun
第二條語句:
mobx.autorun(() => {console.log('張三的賬戶存款:', bankUser.income); });這里出現的 MobX 中的 mobx.autorun 方法對應故事中的 整個A計劃的實施:
autorun 的直觀含義就是 響應式函數 —— 響應觀察值的變化而自動執行指定的函數。
我們看一下其源碼:
附源碼位置:autorun
從這里可以看出 autorun 大致的脈絡如下:
① 首先創建 Reaction 類型對象。new Reaction 操作可以理解為創建探長 R1 ;
探長對應的類是 Reaction,其關鍵特征是 監督并控制任務的執行;
本文的下一節將詳細介紹探長們的 "生活日常",此處先放一放。
② 其次分配任務。源碼中所涉及到的 view() 方法 就是具體的任務內容,即上述故事中的 打印張三賬戶存款 這項任務:
() => {console.log('張三的賬戶存款:', bankUser.income); }③ 最后,立即執行一次部署方案。
代碼中的 reaction.schedule() 表示讓探長 R1 立即執行執行一次部署任務,執行的結果是完成人員部署,并讓探長 R1 打印了一次張三賬戶存款;(同時和觀察員 O1 建立關系)
現在你應該會理解官方文檔中的那句 ”使用 autorun 時,所提供的函數總是立即被觸發一次“ 話了。
看一下 schedule 方法:
看上去很簡單,不到 5 行代碼做了兩件事情:
① 將探長入列;
② 讓隊列中的 所有探長(當然,在我們的示例中僅僅只有 1 名探長)都執行 runReaction 方法
對應時序圖中所標注的 1、2 兩部分:
所謂的 部署(schedule) 就是敦促 各位探長執行 runReaction 方法。
第二條語句從整體上看就這樣了。
接下來就讓我們來詳細分析探長的 runReaction 的方法,在該方法中 探長將聯動觀察員、數據情報室一起在部署方案中發揮監督、自動化響應功能。
2、每位探長的生活日常
任務的執行全靠探長,不過探長的存在常常是 依賴觀察員 的,這是因為在任務過程中,如果想要獲取所監視的張三的存款(income),必須通過觀察員獲取,自身是沒有權力繞過觀察員直接獲取的哦。
每位探長的任務執行流大致如下:
主流程大致只有 4 步:
① 開始執行(runReaction)
② 判斷是否執行(shouldCompute)
③ 執行任務(onInvalidate)
④ 結束
這些基就是每位探長的生活的總體了。下面我們挑其中的第 ① 、 ③ 步來講解。
其實圖中另外有一個很重要的 shouldCompute 判斷方法步驟,根據這個方法探長可以自行判斷 是否執行任務,并非所有的任務都需要執行,這一步的作用是優化 MobX 執行效率。該方法源碼內容先略過,后續章節再展開。2.1、開始執行 - runReaction
該函數比較簡單,主要是為執行任務 ”做一些準備“,給任務營造氛圍。用 startBatch() 開頭,用 endBatch() 結尾,中間隔著 onInvalidate。
startBatch() 和 endBatch() 這兩個方法一定是成對出現,用于影響 globalState 的 inBatch 屬性,表明開啟/關閉 一層新的事務,可以理解為 上下班打卡 操作。
只不過 startBatch() 是 ”上班打卡“,對應時序圖(3.1) 部分:
endBatch() 相當于 “下班打卡”,不過稍微復雜一些,包含一些 收尾 操作,對應時序圖(3.9)部分:
我們繼續看隔在中間的 onInvalidate 方法。?
2.2、執行任務 - onInvalidate
此階段是流程中最重要的階段。
你翻看源碼,將會發現此方法 onInvalidate 是 Reaction 類的一個屬性,且在初始化 Reaction 時傳入到構造函數中的,這樣做的目的是方便做擴展。
所以,autorun 方法本質就是一種預定義好的 Reaction —— 你可以依葫蘆畫瓢,將自定義 onInvalidate 方法傳給 Reaction 來實現自己的 計劃任務(什么 Z計劃啊、阿波羅計劃啊,名字都起好了,就差實現了!!....);
回過頭來,在剛才所述的 autorun 源碼中找到 Reaction 類初始化部分:
const reaction = new Reaction(name, function() {this.track(reactionRunner) })可以看到 onInvalidate 方法就是:
function() {this.track(reactionRunner) }這就不難理解 onInvalidate 實際執行的是 reaction.track 方法。
繼續跟蹤源碼,會發現該 onInvalidate 階段主要是由 3 個很重要的子流程所構成:
- 3.1 跟蹤任務(track)
- 3.2 執行任務(trackDerivedFunction)
- 3.3 更新依賴(bindDependencies)
這 3 個函數并非是并行關系,而是嵌套關系,后者是嵌套在前者內執行的:
題外話:是不是很像 Koa 的 洋蔥圈模型 ??
2.2.1、track
track 方法內容也簡單,和剛才所說的 runReaction 方法類似 —— 也是用 startBatch() 開頭,用 endBatch() 結尾,中間隔著 trackDerivedFunction。
所以在這個案例中,整個部署階段是執行 兩次 startBatch() 和 endBatch() 的;在往后復雜的操作中,執行的次數有可能更多。
我們都知道數據庫中的事務概念,其表示一組原子性的操作。Mobx 則借鑒了 事務 這個概念,它實現比較簡單,就是通過 成對 使用 startBatch 和 endBatch 來開始和結束一個事務,用于批量處理 Reaction 的執行,避免不必要的重新計算。
因此到目前這一步,MobX 程序正處在 第二層 事務中。
MobX 暴露了 transaction 這一底層 API 供用戶調用,讓用戶能夠實現一些較為高級的應用,具體可參考 官方文檔 - Transaction(事務) 章節獲取更多信息。
接下來繼續看隔在中間的 trackDerivedFunction 方法。?
2.2.2、trackDerivedFunction
我們總算到了探長 真正執行任務 的步驟了,之前講的所有流程都是為了這個函數服務的。
該環節的第 1 條語句:
globalState.trackingDerivation = derivation;對應時序圖(3.3):
作用是將 derivation (此處等同于 reaction 對象)掛載到 ”全局變量“ globalState 的 trackingDerivation 屬性上,這樣其他對象就能獲取到該 derivation 對象的數據了。這好比將探長在數據情報室中注冊為 正在執勤人員,后續觀察員 O1 會向數據情報室索取 正在執勤人員 人,然后將自身信息輸送給他 —— 從結果上看,就相當于 觀察員 O1 直接和 探長 R1 匯報;(之所以要經由數據情報室,是因為在執行任務時候,有可能其他工種的人也需要 正在執勤人員 的信息)
該環節的第 2 條語句:
result = f.call(context); // 效果等同于 result = console.log('張三的賬戶存款:', bankUser.income);對應時序圖(3.4):
沒錯,就是本次部署的 終極目的 —— 打印張三賬戶存款!
MobX 將真正的目的執行之前里三層外三層地包裹其他操作,是為了將任務的運行情況控制在自己營造的環境氛圍中。為什么這么做呢?
這么做是基于一個前提,該前提是:所運行的任務 MobX 它無法控制(警署長官今天下達 A 命令,明天下達 B 命令,控制不了)。
所以 MobX 就將任務的執行籠罩在自己所營造的氛圍中,改變不了任務實體,我改變環境總行了吧?!!
由于環境是自己營造的,MobX 可以為所欲為,在環境中穿插各種因素:探長、觀察員、數據情報室等等(后續還有其他角色),這樣就將任務的運行盡最大可能地控制在這套所創造的體系中 —— 孫猴子不也翻不出如來佛的五指山么?
雖然更改不了任務內容,不過 MobX 實際在任務中安插觀察員 O1 了,所以呢,當探長在執行任務時,將觸發時序圖中 (3.5)(3.6)兩步反應:
復雜么?也還好,(3.6)是由 (3.5)觸發的,(3.5)對應的操作是:探長 R1 想要獲取的張三 income 屬性。
(所以,劃重點,敲黑板!! 如果任務中不涉及到 income 這項屬性,那么就不會有 (3.5)的操作,也就沒有 (3.6)什么事)
由于探長 R1 所執行的任務中用到 bankUser.income 變量,這里的 . 符號其實就是 get() 操作;一旦涉及到 get() 操作,監督這個 income 屬性的觀察員 O1 就會執行 reportObserved 方法。 該 reportObserved 方法對應的源碼如下:
那么多行代碼,我們主要關注其中操作影響到探長(derivation)中的操作:
- 更新探長的 lastAccessedBy 屬性(事務 id),這個是為了避免重復操作而設置的
- 更新探長的 newObserving 屬性,將探員信息推入到該隊列中(對應時序圖 (3.6.2)操作),這個比較重要,后續探長和觀察員更新依賴關系就靠這個屬性了;
隨后,任務執行完(時序圖(3.7))后,探長就開始著手更新和觀察員 O1 的關聯關系了。?
2.2.3、bindDependencies
探長 R1 整理和觀察員的關系是在時序圖 (3.8)處:
兩者依賴更新的算法在參考文章Mobx 源碼解讀(四) Reaction 中有詳細的注解,推薦閱讀。這里也做一下簡單介紹。
該函數的目的,是用 derivation.newObserving 去更新 derivation.observing 屬性:
- derivation.newObserving 就是剛才在所述時序圖 (3.6.2)操作是生成的
- 執行完之后 derivation.newObserving 會置空,而 derivation.observing 屬性獲得更新,該屬性反映的 探長 和 觀察員 之間最新的關聯關系;
依賴更新肯定需要遍歷,由于涉及到探長、觀察員兩個維度的數組,樸素算法的時間復雜度將是 O(n^2),而 MobX 中使用 3 次遍歷 + diffValue 屬性的輔助將復雜度降到了 O(n)。? ?
下面我用示例來展現這 3 次遍歷過程。
2.2.3.1、先看一下整體的 input / output
假設在執行 bindDependencies 函數之前, derivation.observing 已有 2 個元素,derivation.newObserving 有 5 個對象(由于 A、B 各重復一次,實際只有 3 個不同的對象 A、B、C),經過 bindDependencies 函數后 derivation.observing 將獲得更新,如下所示:
2.2.3.2、第一次循環:newObserving 數組去重
第一次循環遍歷 newObserving,利用 diffValue 進行去重,一次遍歷就完成了(這種 數組去重算法 可以添加到面試題庫中了??)。注意其中 diffValue 改變情況:
由于 A 對象(引用)既在 observing 數組也在 newObserving 數組中,當改變 newObserving 中 A 元素的 diffValue 值的時候,observing 數組 A 屬性也自然跟著改變;
這次遍歷后,所有 最新的依賴 的 diffValue 值都是 1 了哦,而且去除了所有重復的依賴。
2.2.3.3、第二次循環:去除observing 數組陳舊關聯
接下去第二次遍歷針對 observing 數組,做了兩件事:
- 如果對象的 diffValue 值為 0 (為 0 說明不在 newObserving 數組中,是陳舊的關聯),則調用 removeObserver 去除該關聯;因此這次遍歷之后會刪除 observing 數組中 D 對象
- 讓 observing 數組中剩余對象的 diffValue 值變成 0;
這一次遍歷之后,去除了所有陳舊的依賴,且遺留下來的對象的 diffValue 值都是 0 了。
2.2.3.4、第三次循環:將新增依賴添加到 observing
第二次遍歷針對 newObserving 數組,做了一件事:
- 如果 diffValue 為 1,說明是新增的依賴,調用 addObserver 新增依賴,并將 diffValue 置為 0
這最后一次遍歷,observing 數組關聯都是最新,且 diffValue 都是 0 ,為下一次的 bindDependencies 做好了準備。
至此,A計劃部署方案(autorun 源碼)就講完了。 A 計劃執行后,探長 R1 完成上級下達的任務,同時也和觀察員 O1 建立起明確且牢固的依賴。
3、響應觀察值的變化 - propagateChanged
一旦張三存款發生變化,那么一定會被觀察員 O1 監視到,請問此時觀察員會怎么做?
或許有人會說,觀察員 O1 然后上報給探長 R1 ,然后讓探長 R1 再執行一次打印任務;
從最終結果角度去理解,上面的陳述其實沒毛病,的確是觀察員 O1 驅動探長 R1 再打印一次;
但若從執行過程角度去看,以上陳述是 錯誤的! ?
觀察員 O1 監視到變化之后,的確通知探長 R1了,但探長并非直接執行任務,而是通知 MobX 再按照 A 計劃部署方案執行一遍!;(不得不感慨,這是多么死板地執行機制)
源碼中是怎么體現的呢?
上面提及到過,當觀察員所監控的值(比如income)發生變化時,會觸發 MobX 所提供的 propagateChanged 方法。
propagateChanged 對應的源碼如下:
代碼很簡單,即遍歷觀察員的上級們,讓他們調用 onBecomeStale() 方法 。該觀察員有可能不止對一位上級(上級也不一定只有探長)負責,每位上級的 onBecomeStale() 是不一樣的。(當然,此故事中觀察員 O1 只有 1 位上級 —— 探長 R1)
我們看一下探長這類上級所定義的 onBecomeStale:
onBecomeStale() {this.schedule()}簡單明了,就是直接 再一次執行部署方案。如此簡單樸素,真正做到了 “一視同仁” —— 無論什么任務,一旦部署就緒,任何觀察員反饋情況有變(比如張三賬戶余額發生變動了),探長都是讓 MobX 重新執行一遍部署方案,并不會直接執行任務,反正部署方案中有探長執行任務的步驟嘛。??
所謂的流程化、設計模式,都多多少少在一定程度上約束個體行為(喪失了一部分靈活性),而取得整體上的普適性和可擴展性。
現在再回過頭來看剛才官方文檔截圖中的第二句話:"然后每次它的依賴關系改變時會再次被觸發"
它所表達的意思其實就是:當張三余額發生變化的時候,將 自動觸發 上述的 A 計劃部署方案。
4、小測試
問:下列代碼中 message.title = "Hello world" 為何不會觸發 autorun 再次執行?
const message = observable({ title: "hello" })autorun(() => {console.log(message) })// 不會觸發重新運行 message.title = "Hello world"其實上述問題來自官方的一個問題,若無思路的,可以先參考官方文檔 常見陷阱: console.log。如果能從源碼角度回答這個問題,則說明已經理解本節所講的 autorun 的知識點了
5、小結
此篇是開篇,所闡述的概念僅僅占 MobX 體系冰山一角。
故事中還還有很多問題,比如:
以上問題的答案,讀者可能已經知道,那些還不知道的可以自己留作自己思考;在后續章節,我也會在適當的時機解答上述中的問題;
(也歡迎大家提問,將有可能采納編入后續的故事敘述中來解答)
后續的章節中,將繼續介紹 ComputedValue、Action、Atom、Derivation、Spy 等,正是這些功能角色使得 MobX 有著強大的自動化能力,合理運用了惰性求值、函數式編程等編程范式,使 MobX 在復雜交互應用中大放異彩;
參考文章
羅列本文所參考的文章,感謝他們所給予的幫助:
- 官方中文文檔:不多講,官方文檔最好翻一遍。
- awesome-mobx: MobX 相關資源整合,方便多看多練。
- Mobx 源碼解讀(一) 基本概念:優質的 MobX 源碼解讀文章,受益匪淺。
- MobX 核心源碼解析:本文深入 MobX 源碼來解析其核心原理以及工作流程,推薦閱讀;
- 探秘 MobX:本文短小精悍,主講observable 和 autorun 原理
- MobX 原理:本文對 deviration 著墨較多
總結
以上是生活随笔為你收集整理的【用故事解读 MobX源码(一)】 autorun的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Cloud - 服务消费者
- 下一篇: PHP7 JIT 相关开源项目(JPHP