高性能 MobX 模式(part 2)- 響應變化

譯者:阿里雲前端-也樹

原文鏈接:

  • Part 1 - Shaping the observables
  • Part 2 - Reacting to changes
  • Part 3 - A Cookbook of use cases

Part 1譯文參考:高性能MobX模式(Part 1)

在 Part 1 我們看到如何去建立一個 MobX 狀態樹並且讓它可觀察。在這個基礎上,我們下一步要做的是開始響應數據的變化。坦白說,有趣的部分是從這裡開始的。

MobX 可以保證,無論何時你的響應式數據發生了變化,相應的依賴於 observable 的屬性會自動同步更新。這意味著你現在可以專註於響應變化和處理變化帶來的副作用,而不需要為數據的同步操心。

讓我們深入一下,看看有哪些方式可以讓你處理副作用。

使用 @action 作為入口

默認情況下,當你修改被監聽的變數值,MobX 能夠發覺並且保持其它依賴這個變數的值同步更新。這個過程是同步發生的。然而在很多情況下,你會想要在一個方法里修改多個被監聽的變數值。這會導致許多通知被觸發並且有可能會使你的應用運行變慢。

將你要觸發的方法放在一個 action() 中是一個更好的做法。這會在你的方法上創建一個數據處理的邊界,所有受影響被觀察的變數會在你的 action 執行後保持同步更新。注意這個延遲後的通知只會對當前函數作用域下被觀察的變數生效。如果你有許多會更改被觀察變數的非同步 action,就需要把他們放在 runInAction() 方法里了。

class Person {nn @observable firstName;n @observable lastName;nn // 因為我們用 @action 裝飾器來裝飾這個方法,fullName 只會在 changeName() 執行完成後改變。n @actionn changeName(first, last) {n this.firstName = first;n this.lastName = last;n }nn @computed get fullName() {n return `${this.firstName}, ${this.lastName}`;n }n}nnconst p = new Person();np.changeName(Pavan, Podila);n

action 是 store 產生變化的入口。通過使用 action 方法,你可以讓更新多個被觀察的變數變成原子性的操作。

tip:

儘可能避免直接從外界操作被觀察的變數,同時顯式的為你做數據變化的方法添加 @action 裝飾器。事實上,你可以通過設置 useStrict(true) 來強制禁止在外界操作。

使用 autorun 來觸發副作用

MobX 可以保證被觀察變數的結構是穩定不變的。但是如果 MobX 的世界裡只有被觀察到的變數,是不是太無趣了一點。我們需要它們的一個對應產物 observers 來讓事情有趣一點。

事實上,UI 就是 MobX store 美化後的一名觀察者。通過使用 mobx-react 可以讓你的 React 組件觀察對應 store 的變動並且無論何時 store 發生了變化,這些組件會自動重新渲染。

但是,UI 不是你系統中唯一的觀察者。你可以在你的 store 上添加更多的觀察者,讓它們去做一些有趣的事情。console-logger 可以作為一名最基礎的觀察者,它的功能就是在觀察的變數有變更時,把變更後的值列印出來。

通過 autorun 方法我們可以非常容易的創建一名觀察者。最快的方式就是給 autorun 方法提供一個函數。無論你在這個函數里用到了哪些被觀察的變數,它們都會自動地被 MobX 追蹤。一旦這些變數發生變化,你提供的函數會再次執行,也就是 autorun 的功能。

class Person {nn @observable firstName = None;n @observable lastName = None;nn constructor() {nn // 一個簡單的控制台日誌功能n autorun(()=>{n console.log(`Name changed: ${this.firstName}, ${this.lastName}`);n });nn // 這裡也會觸發一次 autorun n this.firstName = Mob;nn // 這裡又觸發了一次 autorun n this.lastName = X;n }n}nn// 控制台輸出: Name changed: None, Nonen// 控制台輸出: Name changed: Mob, Nonen// 控制台輸出: Name changed: Mob, Xn

上面的日誌你也看到了,autorun方法會立即觸發,並且每一次的觸發都會有追蹤的變數發生變化。但如果你僅僅想要在被觀察的變數有變化的時候觸發,而不想立即去執行 autorun,這個時候要怎麼辦呢?繼續往下讀,我們有 reaction 方法來解決這個問題。

使用 reaction 方法,在初次數據變化後觸發副作用

reaction 相較於 autorun 提供了更符合直覺的控制方式。首先,它不會立即執行,而是會等到追蹤的被觀察變數第一次發生變化後才會執行。它的 API 相較於 autorun 也有微小的區別,最簡單的情況你要提供兩個參數:

reaction(()=>data, data=>{ /* side effect */});n

第一個函數(追蹤函數)需要返回用來追蹤的數據 data。data 會被傳入第二個函數(效果函數)。效果函數是不會被追蹤的,你在這裡可以隨意的使用其它被追蹤的變數。

默認情況下,reaction 第一次是不會執行的,它會等待追蹤函數的變化。只有當追蹤函數返回的 data 發生變化,副作用才會被產生。通過把之前的 autorun 分割為追蹤函數和效果函數,你可以對真正產生副作用的部分有更好的控制。

import {reaction} from mobx;nnclass Router {nn @observable page = main;nn setupNavigation() {n reaction(()=>this.page, (page)=>{n switch(page) {n case main:n this.navigateToUrl(/);n break;nn case profile:n this.navigateToUrl(/profile);n break;nn case admin:n this.navigateToUrl(/admin);n break;n }n });n }nn navigateToUrl(url) { /* ... */ }n}n

在上面的示例中,當我載入 main 頁面的時候,我不想要觸發導航。這是一個使用 reaction 的絕佳示例。只有當 Router 類中的 page 屬性發生變化,才會導航到指定的url。

以上是一個非常簡單的路由功能,只包含了幾個固定的頁面。你可以通過添加一個 page 和 URL 的 map 結構來增強這個路由的可拓展性。通過這種方式,路由(伴隨著 URL 改變)變成了更改你 store 中的某個屬性產生的副作用。

使用 when 來觸發一次性的副作用

autorun 和 reaction 維護的是長期的副作用。當你初始化應用時你可能會創建一次性的副作用並且會期望它們在應用的生命周期內起作用。

有件事我之前一直沒有提到,autorun 和 reaction 都會返回一個 disposer 函數。你可以觸發 disposer 並且在任何時候取消那些副作用。

const disposer = autorun(()=>{ n /* 這裡是副作用,基於追蹤的被觀察變數 */n});nn// .... 之後的某個時間ndisposer(); // 取消 autorunn

現在我們構建的應用有很多使用場景。你可能會僅僅想要在你的應用達到某個條件的時候去觸發特定的副作用。同時你想要這些特定的副作用只出現一次,之後再也不出現。

讓我們看一個具體的例子:比如說,你想要在用戶達到一個里程碑的時候顯示一條信息。這個裡程碑對任何用戶來說只會出現一次,所以你不想創建一個像 autorun 和 reaction這樣的長期維護的副作用。是時候拿出 when 這個 API 來完成這項工作了。

when 需要兩個參數,和 reaction 類似。第一個參數(追蹤函數)需要返回一個布爾值。當返回值為 true,when 方法的第二個參數(效果函數)會執行。最棒的部分是 when 方法在執行之後會自動 dispose。所以沒必要再去定義 disposer 函數同時人工觸發它。

when(()=>this.reachedMilestone, ()=>{n this.showMessage({ title: Congratulations, message: You did it!});n})n

故意忽略的部分

雖然我只提到了 action,autorun ,reaction 和 when 方法,還有一些 MobX 提供給我們的 API 可以用來處理更進階的場景。因為大部分場景下 MobX 只需要使用上面提及的 API,所以我故意暫時忽略了剩下的那些 API。一旦你可以自如的運用上面這些方法,剩下的是非常容易理解和學習的。這些是根基,在開始建設先進和華麗的上層建築之前,我們要把這些爛熟於心。

我可能會為高階 API 單獨寫一篇文章。也許是 Part-4。:-)

一個功能強大的開發套件

到目前為止,我們已經看到過許多去追蹤對象變化並且響應這些變化的技術。Mobx 實現了更高一層的抽象,讓我們可以從更高的層級去思考,而不需要去擔心追蹤和響應變化的意外複雜性。

我們現在有一個依靠領域模型變化的地基,在這之上,我們可以構建健壯的系統。把所有在領域模型之外的部分都當做副作用,我們可以提供視覺反饋(UI),同時也可以提供像監控、數據分析、日誌等等更多的其它功能活動。

未完待續…

在 Part 3,我們會看到很多示例,以便於我們去練習我們目前學到的東西。


推薦閱讀:

如何評價數據流管理框架 MobX ?

TAG:前端开发 | MobX | React |