在Git 找到Mobx 的源碼, 發現其是使用TypeScript 編寫,由於我對Typescrit 沒有項目經驗,因此我先會將其編譯成JavaScript,因此咱們能夠運行以下腳本或者從CDN直接下載一份編譯過的源碼,咱們能夠選擇umd 規範腳本:html
git clone git@github.com:mobxjs/mobx.git
npm i
npm run quick-build
首先咱們從一個最基本的Demo開始,來看Mobx 的基本使用方式:github
const addBtn = document.getElementById('add')
const minusBtn = document.getElementById('minus')
const incomeLabel = document.getElementById('incomeLabel')
const bankUser = mobx.observable({
name: 'Ivan Fan',
income: 3,
debit: 2
});
const incomeDisposer = mobx.autorun(() => {
incomeLabel.innerText = `Ivan Fan income is ${bankUser.income}`
})
addBtn.addEventListener('click', ()=> {
bankUser.income ++
})
minusBtn.addEventListener('click', () => {
bankUser.income --
})
複製代碼
咱們的界面很是簡單,如圖: ajax
從上面的JS文件中,咱們發現其中引用了mobx 兩個方法,分別是observable 和 autorun,是的,是這樣兩個方法,讓incomeLabel 在點擊按鈕的時候實時的發生了變化,因此咱們接下來會對這兩個方法進行深刻分析,這一章節咱們會先分析observable 先進行分析。 咱們先打開Mobx的源碼, 若是咱們用Vscode 打開這個源碼,咱們能夠用快捷鍵Ctrl + K
Ctrl + 0
將代碼都摺疊起來, 而後在打開, 找到exports 的代碼塊,咱們能夠查看mobx 都暴露出了哪些方法:npm
observable,翻譯成中文就是能夠觀測的, 咱們如今來調試這個方法, 咱們能夠const bankUser = mobx.observable({
這一行打一個斷點,而後F11
,跳進去,發現源碼對應的是一個createObservable 方法,也就是建立一個能夠觀察的對象:bash
var observable$$1 = createObservable;
function createObservable(v, arg2, arg3) {
if (typeof arguments[1] === "string") {
return deepDecorator$$1.apply(null, arguments);
}
if (isObservable$$1(v))
return v;
var res = isPlainObject$$1(v)
? observable$$1.object(v, arg2, arg3)
: Array.isArray(v)
? observable$$1.array(v, arg2)
: isES6Map$$1(v)
? observable$$1.map(v, arg2)
: v;
if (res !== v)
return res;
// otherwise, just box it
fail$$1(process.env.NODE_ENV !== "production" &&
"The provided value could not be converted into an observable. If you want just create an observable reference to the object use 'observable.box(value)'");
}
複製代碼
上面代碼很簡單,參數有三個,可是咱們在調用的時候,值傳遞了一個參數, 因此咱們暫且只要關心第一個參數 r .如下是這個functin 的基本邏輯:app
observable$$1.object
, observable$$1.array
, observable$$1.map
方法, 那這個observable$$1又是什麼呢?在第一行var observable$$1 = createObservable;
表面就是createObservable方法。可是這個方法就短短几行代碼,並無object, array, map着三個方法, 咱們發如今這個方法下面有observableFactories 對象,其是一個工廠對象,用來給createObservable添加方法,其定義了這三個方法,而且通遍歷過Object.keys(observableFactories).forEach(function (name) { return (observable$$1[name] = observableFactories[name]); });
由於在咱們的Demo 中咱們傳遞的是一個Object, 因此會調用observable$$1.object
方法,接下來咱們在繼續分析這個方法, 其代碼以下:ide
object: function (props, decorators, options) {
if (typeof arguments[1] === "string")
incorrectlyUsedAsDecorator("object");
var o = asCreateObservableOptions$$1(options);
if (o.proxy === false) {
return extendObservable$$1({}, props, decorators, o);
}
else {
var defaultDecorator = getDefaultDecoratorFromObjectOptions$$1(o);
var base = extendObservable$$1({}, undefined, undefined, o);
var proxy = createDynamicObservableObject$$1(base);
extendObservableObjectWithProperties$$1(proxy, props, decorators, defaultDecorator);
return proxy;
}
},
複製代碼
var o = asCreateObservableOptions$$1(options);
生成的了一個簡單的對象:ui
var defaultCreateObservableOptions$$1 = {
deep: true,
name: undefined,
defaultDecorator: undefined,
proxy: true
};
複製代碼
o.proxy
的值爲true
, 因此會走else
邏輯分支, 因此接下來咱們一一分析else
分支中的每一條代碼。spa
var defaultDecorator = getDefaultDecoratorFromObjectOptions$$1(o);
這個是跟裝飾器有關的邏輯,咱們先跳過var base = extendObservable$$1({}, undefined, undefined, o);
對o對象進行了加工處理,變成了一個Symbol
數據類型。這一步操做很是重要,給一個空對象添加了一個
$mobx$$1
(var $mobx$$1 = Symbol("mobx administration");
)的屬性, 其值是一個ObservableObjectAdministration
類型對象,其write
方法在後續數據攔截中會調用。
var proxy = createDynamicObservableObject$$1(base);
這個方法,最爲核心, 對這個對象進行了代理(Proxy)對這個對象的屬性的get
, set
, has
, deleteProperty
, ownKeys
, preventExtensions
方法進行了代理攔截,這個是Mobx 事件數據添加的一個核心點。
第三點的proxy
其實只是初始化了一個簡單的代理對象,可是沒有與咱們須要觀察的target
(也就是mobx.observable
方法傳遞進來的須要被觀察的對象)關聯起來, extendObservableObjectWithProperties$$1(proxy, props, decorators, defaultDecorator);
方法會遍歷target
的屬性,將其賦值給proxy
對象, 而後咱們mobx.observable
裏的對象都被代理了,也就是實現了對屬性操做的攔截處理。
在第四點extendObservableObjectWithProperties$$1
方法中, 最終會給原始的對象的屬性進行裝飾,經過查看function 的 call stack 得知,最後對調用ObservableObjectAdministration 的addObservableProp 方法, 針對每個propName(原始對象的Key)生成一個ObservableValue 對象,而且保存在ObservableObjectAdministration 對象的values中
從圖三中發現, 真正實現數據攔截的就是objectProxyTraps
攔截器, 下一章節,咱們須要對這個攔截器進行深刻分析,着重看get
,set
如何實現了數據攔截。
return proxy;
最終將返回一個已經被代理過的對象,替換原生對象。bankUser 對象就是一個已經被代理了的對象,而且包含了一個
Symbol
類型的新的屬性。
const bankUser = mobx.observable({
name: 'Ivan Fan',
income: 3,
debit: 2
});
複製代碼
array
, map
, object
, 如今只分析object 類型的狀況)var base = extendObservable$$1({}, undefined, undefined, o);
), 包括一個Symbol
類型的屬性,其值是一個ObservableObjectAdministration
類型的對象.get
, set
...) var proxy = new Proxy(base, objectProxyTraps);
bankUser