使用 react 寫小型應用,數據、業務邏輯和視圖的模塊劃分不是很細是沒有問題的。在這個階段,引入任何狀態管理庫,都算是奢侈的。可是隨着頁面邏輯的複雜度提高,在中大型應用中,數據、業務邏輯和視圖,若是不能很好的劃分,就頗有可能出現維護難、性能低下的問題。javascript
業內比較成熟的解決方案有 redux,可是 redux 使用過程當中,給個人感受是太複雜和繁瑣。那麼爲何不簡單一點呢?mobx 的核心理念是 簡單、可擴展的狀態管理庫。這可能正是你想要的。java
react 關注的狀態(state)到視圖(view)的問題。而 mobx 關注的是狀態倉庫(store)到的狀態(state)的問題。react
mobx 最最核心的概念只有2個。 @observable
和 @observer
,它們分別對應的是被觀察者和觀察者。這是你們常見的觀察者模式,不過這裏使用了,ES7 中的 裝飾器。es6
使用 @observable
能夠觀察類的值。redux
這裏使用 @observable
將 Store 的 todos
變爲一個被觀察的值。瀏覽器
倉庫緩存
// 這裏引入的是 mobx import {observable} from 'mobx'; class Store { @observable todos = [{ title: "todo標題", done: false, }]; }
mobx 組件dom
而後再使用 @observer
,將組件變爲觀察者,響應 todos
狀態變化。
當狀態變化時,組件也會作相應的更新。函數
// 這裏引入的是 mobx-react import {observer} from 'mobx-react'; @observer class TodoBox extends Component { render() { return ( <ul> {this.props.store.todos.map(todo => <li>{todo.title}</li>)} </ul> ) } }
完整的 demo 以下。性能
import React, {Component} from 'react'; import { render } from 'react-dom'; import {observable} from 'mobx'; import {observer} from 'mobx-react'; // 最簡單的 mobx 就是一個觀察者模式 class Store { // 被觀察者 @observable todos = [{ title: "完成 Mobx 翻譯", done: false, }]; } // 觀察者 @observer class TodoBox extends Component { render() { return ( <ul> {this.props.store.todos.map(todo => <li>{todo.title}</li>)} </ul> ) } } const store = new Store(); render( <TodoBox store={store} />, document.getElementById('root') );
經過以上的簡單的例子,展示了 mobx 分離數據、視圖的能力。
這一小節要介紹的兩個概念雖然也是核心概念,可是是可選的。
前面例子,只講了狀態的讀取,那麼狀態應該如何寫入呢?
答案是直接寫入!
@observer class TodoBox extends Component { render() { return ( <div> <ul> {this.props.store.todos.map( (todo,index) => <li key={index}>{todo.title}</li> )} </ul> <div> <input type="button" onClick={() => { // 直接修改倉庫中的狀態值 this.props.store.todos[0].title = "修改後的todo標題" }} value="點我"/> </div> </div> ) } }
細心的朋友必定發現了奇怪的地方,react 官方說過 props
值不能直接修改,可是引入 mobx 後 props
能夠直接修改了,這太奇怪了!
解決辦法就是 mobx 的下一個概念 action
。
首先在 Store 中,定義一個 action。
class Store { @observable todos = [{ title: "todo標題", done: false, }]; @action changeTodoTitle({index,title}){ this.todos[index].title = title } }
在 Component 中調用,這樣經過 action 的方法,就避免了直接修改 props
的問題。
<input type="button" onClick={() => { this.props.store.changeTodoTitle({index:0,title:"修改後的todo標題"}); }} value="點我"/>
能夠經過引入 mobx 定義的嚴格模式,強制使用 action 來修改狀態。
import {useStrict} from 'mobx'; useStrict(true);
在有些時候,state 並不必定是咱們須要的最終數據。例如,全部的 todo 都放在 store.todos 中,而已經完成的 todos 的值(store.unfinishedTodos),能夠由 store.todos 衍生而來。
對此,mobx 提供了 computed
裝飾器,用於獲取由基礎 state 衍生出來的值。若是基礎值沒有變,獲取衍生值時就會走緩存,這樣就不會引發虛擬 DOM 的從新渲染。
經過 @computed
+ getter
函數來定義衍生值(computed values)。
import { computed } from 'mobx'; class Store { @observable todos = [{ title: "todo標題", done: false, },{ title: "已經完成 todo 的標題", done: true, }]; @action changeTodoTitle({index,title}){ this.todos[index].title = title } @computed get finishedTodos () { return this.todos.filter((todo) => todo.done) } }
mobx 有一套機制,若是衍生值(computed values)所依賴的基礎狀態(state)沒有發生改變,獲取衍生值時,不會從新計算,而是走的緩存。所以 mobx 不會引發過分渲染,從而保障了性能。
當渲染的值爲 finishedTodos ,點擊修改標題,不會在控制檯打印 "render";
換成 todos,就會打印 "render".
這是因爲已完成的 todos 值沒有改變,因此不會從新計算,而是走的緩存。所以不會調用 render 方法。
完整 demo 以下
import React, {Component} from 'react'; import { render } from 'react-dom'; import {observable, action, computed,useStrict} from 'mobx'; import {observer} from 'mobx-react'; useStrict(true); class Store { @observable todos = [{ title: "todo標題", done: false, },{ title: "已經完成 todo 的標題", done: true, }]; @action changeTodoTitle({index,title}){ this.todos[index].title = title } @computed get unfinishedTodos () { return this.todos.filter((todo) => todo.done) } } @observer class TodoBox extends Component { render() { console.log('render'); return ( <div> <ul> { /* 把 unfinishedTodos 換成 todos,點擊修改標題就會在控制檯打印 "render".*/ } {this.props.store.unfinishedTodos.map( (todo,index) => <li key={index}>{todo.title}</li> )} </ul> <div> <input type="button" onClick={() => { this.props.store.changeTodoTitle({index:0,title:"修改後的todo標題"}); }} value="修改標題"/> </div> </div> ) } } const store = new Store(); render( <TodoBox store={store} />, document.getElementById('root') );
翻譯了官網的一段文章,就拿過來作小結了。
mobx 是一個的簡單、可擴展的狀態管理庫。它背後的哲學很是簡單:
應用程序 state 是最基礎的數據。任何能夠從 state 中衍生出來的數據,都應該自動的被衍生出。
actions 是惟一可以改變 state 的方法。
state 是最基礎的數據,它不該該包含冗餘的和派生的數據。
computed values 派生值是經過純函數從 state 中派生而來的。當派生值依賴的狀態發生變化了,Mobx 將會自動更新派生值。若是依賴的狀態沒有改變,mobx 會作優化處理。
reactions 也是派生數據,是從 state 中派生而來的。它的反作用是自動更新 UI。(注:mobx 有一個 reaction 接口,當 state 改變時,就會調用它的回調。UI 是經過 reaction 更新的。)
React 和 MobX 是很是強大的組合。React 提供了將應用狀態映射爲可渲染的組件樹的機制。MobX 提供存儲和更新應用狀態的機制,供 React 使用。
React 和 MobX 提供了開發過程當中常見問題的解決方案。 React 經過使用虛擬 DOM,減小了對瀏覽器 DOM 的操做。MobX 經過使用了響應式虛擬依賴狀態圖(reactive virtual dependency state graph) ,提供了應用程序狀態與 React 組件同步的機制,這樣 state 只會在須要時更新纔會更新。(譯者注:這段有點難理解,大概的意思是 Mobx 關注的是 store 到 state 的過程,React 關注的是 state 到 view 的過程)。
在實際開發中,須要用到很多 mobx 的輔助函數,這些輔助函數一共 14 個,挑了一些列舉以下。
autorun
observable 的值初始化或改變時,自動運行。
trasaction
批量改變時,經過 trasaction 包裝,只會觸發一次 autorun。
extendsObservable
對類的屬性或實例,進行監聽。
observable
對普通對象進行監聽。
map
使用 asMap 將對象轉化爲 map。
action-strict
在 mobx.usrStrict(true)時,只能經過 action 觸發值的改變。
when
相似 autorun.
mobx.when 第一個參數是一個函數,初始化時也會自動執行。該函數返回一個 boolean 值,當返回值爲 true 的時候,纔會繼續觸發第一個函數。當返回值爲 flase 時,再也不繼續監聽。這時會執行 mobx.when 的第二個參數,這個參數也是一個函數。
reaction
相似 autorun.
reaction 不會在初始化時執行,只會在值改變的時候執行。
該函數有 2 個值,第一個參數是一個函數,返回監聽的值.
第二個參數,也是一個函數,會在值改變的時候執行。
spy
相似 aoturun.
監聽全部 mobx 的事件。
包含一個 type ,該值用來區分執行事件的類型。
whyRun用於調試,打印 autorun 爲何會觸發。