Mobx-React : 當前最適合React的狀態管理工具
MobX
簡單、可擴充套件的狀態管理
MobX 是由 Mendix、Coinbase、Facebook 開源和眾多個人贊助商所贊助的。
安裝
- 安裝:
npm install mobx --save
。 React 繫結庫:npm install mobx-react --save
。 要啟用 ESNext 的裝飾器 (可選), 參見下面。 - CDN:
- https://unpkg.com/mobx/lib/mobx.umd.js
- https://cdnjs.com/libraries/mobx
瀏覽器支援
- MobX >=5 版本執行在任何支援 ES6 proxy 的瀏覽器。如果執行在像 IE11、Node.js 6 以下版本或依靠與較舊的 JavaScripCore 的安卓端的 React Native (點選檢視如何升級])。
- MobX 4 可以執行在任何支援 ES5 的瀏覽器上,而且也將進行持續地維護。MobX 4 和 5 的 API 是相同的,並且語義上也能達到相同的效果,只是 MobX 4 存在一些 侷限性。
小貼士: MobX 5 包的主入口點附帶 ES5 程式碼,以便向後相容所有構建工具。但因為 MobX 5 只能執行在現代瀏覽器上,所以可以考慮使用速度最快、體積最小的 ES6 構建: lib/mobx.es6.js
resolve: { alias: { mobx: __dirname + "/node_modules/mobx/lib/mobx.es6.js" }}
入門指南
- egghead.io 課程
- 十分鐘互動式的 MobX + React 教程
- 由 Pavan Podila 和 Michel Weststrate 撰寫的 MobX 書籍 (非常不喜歡xx深入的書名!)
- MobX 4官方文件和API概覽 (MobX 3, MobX 2)
- 視訊:
- ReactNext 2016: 真實世界的 MobX - 40分鐘 幻燈片
- React 和 MobX 實戰. OpenSourceNorth 開發者大會上,Matt Ruby 深入介紹和說明如何使用MobX和React(ES5版本) - 42分鐘
- LearnCode.academy MobX 教程 第一部分: MobX + React 太棒了 (7分鐘) 第二部分: Computed Values and 巢狀/引用的 Observables (12分鐘)
- 錄播: MobX 介紹 - 8分鐘
- 訪談: 狀態管理很容易 - React Amsterdam 2016 開發者大會 (幻燈片)
- 樣板檔案和相關專案
- 更多教程、部落格和視訊盡在 MobX 主頁
- 更多教程、部落格、視訊和其他有用的資源盡在 Awesome MobX
入門
MobX 是一個經過戰火洗禮的庫,它通過透明的函式響應式程式設計(transparently applying functional reactive programming - TFRP)使得狀態管理變得簡單和可擴充套件。MobX背後的哲學很簡單:
任何源自應用狀態的東西都應該自動地獲得。
其中包括UI、資料序列化、伺服器通訊,等等。
React 和 MobX 是一對強力組合。React 通過提供機制把應用狀態轉換為可渲染元件樹並對其進行渲染。而MobX提供機制來儲存和更新應用狀態供 React 使用。
對於應用開發中的常見問題,React 和 MobX 都提供了最優和獨特的解決方案。React 提供了優化UI渲染的機制, 這種機制就是通過使用虛擬DOM來減少昂貴的DOM變化的數量。MobX 提供了優化應用狀態與 React 元件同步的機制,這種機制就是使用響應式虛擬依賴狀態圖表,它只有在真正需要的時候才更新並且永遠保持是最新的。
核心概念
MobX 的核心概念不多。 下面的程式碼片段可以在 codesandbox 示例中線上試用。
Observable state(可觀察的狀態)
Egghead.io 第1課: observable & observer
MobX 為現有的資料結構(如物件,陣列和類例項)添加了可觀察的功能。 通過使用 @observable 裝飾器(ES.Next)來給你的類屬性添加註解就可以簡單地完成這一切。
1 import { observable } from "mobx"; 2 3 class Todo { 4 id = Math.random(); 5 @observable title = ""; 6 @observable finished = false; 7 }
使用 observable
很像把物件的屬性變成excel的單元格。 但和單元格不同的是,這些值不只是原始值,還可以是引用值,比如物件和陣列。
如果你的環境不支援裝飾器語法,也不必擔心。 你可以點選這裡檢視如何進行設定。 或者你可以直接跳過設定,因為 MobX 可以通過 decorate 工具在不支援裝飾器語法的情況加使用。 儘管如此,多數 MobX 使用者更喜歡裝飾器語法,因為它更簡潔。
例如,上面一段程式碼的ES5版本應該是這樣:
1 import { decorate, observable } from "mobx"; 2 3 class Todo { 4 id = Math.random(); 5 title = ""; 6 finished = false; 7 } 8 decorate(Todo, { 9 title: observable, 10 finished: observable 11 })
Computed values(計算值)
Egghead.io 第3課: 計算值
使用 MobX, 你可以定義在相關資料發生變化時自動更新的值。 通過@computed
裝飾器或者利用 (extend)Observable
時呼叫 的getter / setter 函式來進行使用。(當然,這裡也可以再次使用 decorate
來替代 @
語法)。
1 class TodoList { 2 @observable todos = []; 3 @computed get unfinishedTodoCount() { 4 return this.todos.filter(todo => !todo.finished).length; 5 } 6 }
當添加了一個新的todo或者某個todo的 finished
屬性發生變化時,MobX 會確保 unfinishedTodoCount
自動更新。 像這樣的計算可以類似於 MS Excel 這樣電子表格程式中的公式。每當只有在需要它們的時候,它們才會自動更新。
Reactions(反應)
Egghead.io 第9課: 自定義反應
Reactions 和計算值很像,但它不是產生一個新的值,而是會產生一些副作用,比如列印到控制檯、網路請求、遞增地更新 React 元件樹以修補DOM、等等。 簡而言之,reactions 在 響應式程式設計和指令式程式設計之間建立溝通的橋樑。
React 元件
Egghead.io 第1課: observable & observer
如果你用 React 的話,可以把你的(無狀態函式)元件變成響應式元件,方法是在元件上新增 observer
函式/ 裝飾器. observer
由 mobx-react
包提供的。
1 import React, {Component} from 'react'; 2 import ReactDOM from 'react-dom'; 3 import {observer} from 'mobx-react'; 4 5 @observer 6 class TodoListView extends Component { 7 render() { 8 return <div> 9 <ul> 10 {this.props.todoList.todos.map(todo => 11 <TodoView todo={todo} key={todo.id} /> 12 )} 13 </ul> 14 Tasks left: {this.props.todoList.unfinishedTodoCount} 15 </div> 16 } 17 } 18 19 const TodoView = observer(({todo}) => 20 <li> 21 <input 22 type="checkbox" 23 checked={todo.finished} 24 onClick={() => todo.finished = !todo.finished} 25 />{todo.title} 26 </li> 27 ) 28 29 const store = new TodoList(); 30 ReactDOM.render(<TodoListView todoList={store} />, document.getElementById('mount'));
observer
會將 React (函式)元件轉換為它們需要渲染的資料的衍生。 使用 MobX 時沒有所謂的智慧和無腦元件。 所有的元件都會以巧妙的方式進行渲染,而只需要一種簡單無腦的方式來定義它們。MobX 會確保元件總是在需要的時重新渲染,但僅此而已。所以上面例子中的 onClick
處理方法會強制對應的 TodoView
進行渲染,如果未完成任務的數量(unfinishedTodoCount)已經改變,它將導致 TodoListView
進行渲染。 可是,如果移除 Tasks left
這行程式碼(或者將它放到另一個元件中),當點選 checkbox
的時候 TodoListView
就不再重新渲染。你可以在 JSFiddle 中自己動手來驗證這點。
自定義 reactions
使用autorun
、reaction
和 when
函式即可簡單的建立自定義 reactions,以滿足你的具體場景。
例如,每當 unfinishedTodoCount
的數量發生變化時,下面的 autorun
會列印日誌訊息:
autorun(() => {
console.log("Tasks left: " + todos.unfinishedTodoCount)
})
MobX 會對什麼作出響應?
為什麼每次 unfinishedTodoCount
變化時都會列印一條新訊息?答案就是下面這條經驗法則:
MobX 會對在執行跟蹤函式期間讀取的任何現有的可觀察屬性做出反應。
想深入瞭解 MobX 是如何知道需要對哪個可觀察屬性進行響應,請查閱 理解 MobX 對什麼有反應。
Actions(動作)
Egghead.io 第5課: actions
不同於 flux 系的一些框架,MobX 對於如何處理使用者事件是完全開明的。
- 可以用類似 Flux 的方式完成
- 或者使用 RxJS 來處理事件
- 或者用最直觀、最簡單的方式來處理事件,正如上面演示所用的
onClick
最後全部歸納為: 狀態應該以某種方式來更新。
當狀態更新後,MobX
會以一種高效且無障礙的方式處理好剩下的事情。像下面如此簡單的語句,已經足夠用來自動更新使用者介面了。
從技術上層面來講,並不需要觸發事件、呼叫分派程式或者類似的工作。歸根究底 React 元件只是狀態的華麗展示,而狀態的衍生由 MobX 來管理。
1 store.todos.push( 2 new Todo("Get Coffee"), 3 new Todo("Write simpler code") 4 ); 5 store.todos[0].finished = true;
儘管如此,MobX 還是提供了 actions
這個可選的內建概念。 如果你現在就想要了解如何編寫 actions,請閱讀 Actions 章節。很簡單! 使用 actions
是有優勢的: 它們可以幫助你把程式碼組織的更好,還能在狀態何時何地應該被修改這個問題上幫助你做出明智的決定。
MobX: 簡單且可擴充套件
MobX 是狀態管理庫中侵入性最小的之一。這使得 MobX
的方法不但簡單,而且可擴充套件性也非常好:
使用類和真正的引用
使用 MobX 不需要使資料標準化。這使得庫十分適合那些異常複雜的領域模型(以 Mendix 為例: 一個應用中有大約500個領域類)。
保證參照完整性
因為資料不需要標準化,所以 MobX 會自動跟蹤狀態和衍生之間的關係,你可以免費獲得參照完整性。渲染通過三級間接定址訪問的資料?
沒有問題,MobX 會跟蹤它們,一旦其中一個引用發生了變化,就會重新渲染。作為回報,陳年的老bug已不復存在。作為一個程式設計師,你可能記不住修改的一些資料可能會影響到的某個角落裡看起來毫不相關的元件,但 MobX 不會。
更簡單的 actions 更便於維護
正如上面所演示的,使用 MobX 修改狀態是非常簡單的。你只需簡單的寫出你的目的。MobX 會替你處理好剩下的事情。
細粒度的可觀測性是高效的
MobX 構建應用中所有衍生的圖形,以找到保持最新狀態所需的重新計算的最少次數。“衍生一切”或許聽上去開銷很昂貴,但 MobX 構建虛擬衍生圖以保持衍生與狀態同步所需的重計算的數量最小化。
事實上,在 Mendix 測試 MobX 時我們發現使用這個庫跟蹤程式碼中的關係通常會更有效,而不是通過使用手寫事件或基於容器元件的“智慧”選擇器來推送更改。
簡單來說,是因為 MobX 會在資料上建立更細粒度的“監聽器”,而不是通過程式來控制。
其次, MobX 看到衍生之間的因果關係,因此它可以為衍生排序,使得衍生不會執行多次或引入缺陷。
想了解這是如何工作的? 請參見 深入剖析 MobX。
易操作性
MobX 使用原生 javascript 。由於它的侵入性不強,它可以和絕大部分 javascript 庫共同使用,而不需要特定的 MobX 風格庫。
所以你可以繼續使用你的路由,資料獲取和工具庫,比如react-router
、 director
、 superagent
、 lodash
,等等。
出於同樣的原因,你可以在伺服器端和客戶端使用它,也可以在 react-native 這樣的同構應用中使用。
結論就是: 相比其它狀態管理解決方案,當使用 MobX 時通常只需學習更少的新概