Mobx 思想的實現原理

ascoders發表於2017-03-11

Mobx 最關鍵的函式在於 autoRun,舉個例子,它可以達到這樣的效果:

我們發現這個函式非常智慧,用到了什麼屬性,就會和這個屬性掛上鉤,從此一旦這個屬性發生了改變,就會觸發回撥,通知你可以拿到新值了。沒有用到的屬性,無論你怎麼修改,它都不會觸發回撥,這就是神奇的地方。

autoRun 的用途

使用 autoRun 實現 mobx-react 非常簡單,核心思想是將元件外面包上 autoRun,這樣程式碼中用到的所有屬性都會像上面 Demo 一樣,與當前元件繫結,一旦任何值發生了修改,就直接 forceUpdate,而且精確命中,效率最高。

依賴收集

autoRun 的專業名詞叫做依賴收集,也就是通過自然的使用,來收集依賴,當變數改變時,根據收集的依賴來判斷是否需要更新。

實現步驟拆解

為了相容,Mobx 使用了 Object.defineProperty 攔截 gettersetter,但是無法攔截未定義的變數,為了方便,我們使用 proxy 來講解,而且可以監聽未定義的變數哦。

步驟一 儲存結構

眾所周知,事件監聽是需要預先儲存的,autoRun 也一樣,為了知道當變數修改後,哪些方法應該被觸發,我們需要一個儲存結構。

首先,我們需要儲存所有的代理物件,讓我們無論拿到原始物件,還是代理物件,都能快速的找出是否有對應的代理物件存在,這個功能用在判斷代理是否存在,是否合法,以及同一個物件不會生成兩個代理。

程式碼如下:

重點來了,第二個要儲存的是最重要的部分,也就是所有監聽!當任何物件被改變的時候,我們需要知道它每一個 key 對應著哪些監聽(這些監聽由 autoRun 註冊),也就是,最終會存在多個物件,每個物件的每個 key 都可能與多個 autoRun 繫結,這樣在更新某個 key 時,直接觸發與其繫結的所有 autoRun 即可。

程式碼如下:

第三個儲存結構就是待觀察佇列,為了使同一個呼叫棧多次賦值僅執行一次 autoRun,所有待執行的都會放在這個佇列中,在下一時刻統一執行佇列並清空,執行的時候,當前所有 autoRun 都是在同一時刻觸發的,所以讓相同的 autoRun 不用觸發多次即可實現效能優化。

程式碼如下:

我們還要再儲存兩個全域性變數,分別是是否在佇列執行中,以及當前執行到的 autoRun

程式碼如下:

步驟二 將物件加工可觀察

這一步講解的是 observable 做了哪些事,首先第一件就是,如果已經存在代理物件了,就直接返回。

程式碼如下:

我們繼續看 toObservable 函式,它做的事情是,例項化代理,並攔截 get set 等方法。

我們先看攔截 get 的作用:先拿到當前要獲取的值 result,如果這個值在代理中存在,優先返回代理物件,否則返回 result 本身(沒有引用關係的基本型別)。

上面的邏輯只是簡單返回取值,並沒有註冊這一步,我們在 currentObserver 存在時才會給物件當前 key 註冊 autoRun,並且如果結果是物件,又不存在已有的代理,就呼叫自身 toObservable 再遞迴一遍,所以返回的物件一定是代理。

registerObserver 函式的作用是將 targetObj -> key -> autoRun 這個鏈路關係存到 observers 物件中,當物件修改的時候,可以直接找到對應 keyautoRun

那麼 currentObserver 是什麼時候賦值的呢?首先,並不是訪問到 get 就要註冊 registerObserver,必須在 autoRun 裡面的才符合要求,所以執行 autoRun 的時候就會將當前回撥函式賦值給 currentObserver,保證了在 autoRun 函式內部所有監聽物件的 get 攔截器都能訪問到 currentObserver。以此類推,其他 autoRun 函式回撥函式內部變數 get 攔截器中,currentObserver 也是對應的回撥函式。

程式碼如下:

setter 過程中,如果物件產生了變動,就會觸發 queueObservers 函式執行回撥函式,這些回撥都在 getter 中定義好了,只需要把當前物件,以及修改的 key 傳過去,直接觸發對應物件,當前 key 所註冊的 autoRun 即可。

程式碼如下:

沒錯,主要邏輯已經全部說完了,新物件之所以可以檢測到,是因為 proxyget 會觸發,這要多謝 proxy 的強大。

可能有人問 Object.defineProperty 為什麼不行,原因很簡單,因為這個函式只能設定某個 keygetter setter~。

symbol proxy reflect 這三劍客能做的事還有很多很多,這僅僅是實現 Object.observe 而已,還有更強大的功能可以挖掘。

總結

es6 真的非常強大,呼籲大家拋棄 ie11,擁抱美好的未來!

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

Mobx 思想的實現原理 Mobx 思想的實現原理

相關文章