原文地址:https://mobxjs.github.io/mobx/getting-started.htmlhtml
寫在前面:本人英語水平有限,主要是寫給本身看的,如有哪位同窗看到了有問題的地方,請爲我指出,很是感謝;react
mobx是一個比redux更好的狀態管理包,代碼量更少,思路更清晰,沒有像redux那樣複雜的reducer,action (ps: redux的做者也推薦mobx,某大大告訴個人,並無原話連接)git
1.mobx 反應流程github
2.the core idearedux
State 是每個應用程序的核心部分,而使用一個不合規範的 State 則是讓你的應用充滿 bug 和失控的不二法門,或者就是局部變量環繞,讓你的 state 失去了同步。有不少框架試圖解決這個問題,好比使用不可變的 state,可是這樣以來又帶來了新的問題,好比數據必須規格化,完整性約束失效等等。而且它變得不可能使用強大的概念,如原型。數組
MobX 讓整個事情又變簡單了:它不容許產生失控的 state。它的理念也很簡單:全部能夠從 state 中派生的事物,都會自動的派生。網絡
從概念上講,Mobx會像Excel 表格同樣處理您的應用程序。架構
理論說的夠多的了,看一個例子也許會更明白一些。咱們從一個簡單的 todo 程序開始。app
爲了原創性,讓咱們從一個很是簡單的ToDoStore開始。下面是一個很是直接的TodoStore,它維護着一個todos的集合。沒有MobX參與。框架
class TodoStore { todos = []; get completedTodosCount() { return this.todos.filter( todo => todo.completed === true ).length; } report() { if (this.todos.length === 0) return "<none>"; return `Next todo: "${this.todos[0].task}". ` + `Progress: ${this.completedTodosCount}/${this.todos.length}`; } addTodo(task) { this.todos.push({ task: task, completed: false, assignee: null }); } } const todoStore = new TodoStore();
咱們剛剛建立了一個帶有todos集合的todoStore實例。用一些對象填充todoStore。爲了確保咱們看到咱們的更改的影響,咱們在每次更改後調用todoStore.report並記錄它。
請注意,報表有意老是隻打印第一個任務。它使這個例子有點人工,但正如你將看到下面它很好地演示了MobX的依賴關係跟蹤是動態的。
===》
3.Becoming reactive
到目前爲止,這段代碼沒有什麼特別之處。可是若是咱們能夠再也不手動調用 report 方法,只聲明咱們但願在每次狀態更改時調用它?咱們只須要在想要的地方修改這個 state,全部的彙報都自動來作。
幸運的是,這正是MobX能夠爲你作的。自動執行徹底取決於狀態的代碼。這樣咱們的報表功能就會自動更新,就像電子表格中的圖表同樣。爲了實現這一點,TodoStore必須成爲可觀察的(observable),以便MobX能夠跟蹤全部正在進行的更改。讓咱們改變類就足以實現它。
同時,completedTodosCount 屬性應該被自動派生。經過使用@observable和@computed裝飾器,咱們能夠在對象上引入observable屬性:
class ObservableTodoStore { @observable todos = []; @observable pendingRequests = 0; constructor() { mobx.autorun(() => console.log(this.report)); } @computed get completedTodosCount() { return this.todos.filter( todo => todo.completed === true ).length; } @computed get report() { if (this.todos.length === 0) return "<none>"; return `Next todo: "${this.todos[0].task}". ` + `Progress: ${this.completedTodosCount}/${this.todos.length}`; } addTodo(task) { this.todos.push({ task: task, completed: false, assignee: null }); } } const observableTodoStore = new ObservableTodoStore();
至此!咱們將一些屬性標記爲@observable,以指示MobX這些值能夠隨時間改變。計算用@computed來裝飾,以識別這些能夠從狀態導出。
pendingRequests和assignee屬性到目前爲止未使用,但將在本教程後面使用。爲了簡潔,本頁上的全部示例都使用ES6,JSX和裝飾器。但不要擔憂,全部的裝飾在MobX有一個ES5寫法。
在構造函數中,咱們建立了一個report()將其包裝在mobx.autorun()。mobx.autorun()建立一個運行一次的reaction,而後每當在函數內部使用的任何可觀察數據發生變化時,自動從新運行。由於report()用observable todos屬性,因此它會在適當時打印報表。這在下一個列表中演示。只需按下運行按鈕:
==>
純函數,對吧?report自動打印了,同步而且沒有泄漏的中間值。若是你仔細查看運行結果的話,你會發現咱們的第四句語句沒有產生輸出,由於咱們修改了todos[1]的數據,而咱們在report中指明的數據,並無todos[1]的變化而發生變化。而第五句話修改了todos[0]的數據則輸出了。這個例子很好的說明了,autorun不是簡單的監視了todos,而是精確到了具體的一項。
4.Making React reactive
Ok, so far we made a silly report reactive. Time to build a reactive user interface around this very same store. React components are (despite their name) not reactive out of the box. The @observer
decorator from the mobx-react
package fixes that by wrapping the React component render
method in autorun
, automatically keeping your components in sync with the state. That is conceptually not different from what we did with the report
before.
好了,到目前爲止咱們作了一個愚蠢的 report reactive。是時候用很是類似的store建立一個reactive了。React components(儘管他們的名字)沒有反應開箱。
mobx-react包中的@observer裝飾器經過在mobx.autorun()包裝React組件的render()方法來實現,自動保持組件與狀態同步。這在概念上與咱們之前的報告沒有什麼不一樣
下面的清單定義了幾個React組件。在那裏惟一屬於MobX的東西是@observer裝飾器。這足以確保每一個組件在相關數據更改時單獨從新render。您不須要再調用setState,也沒必要了解如何使用須要配置的選擇器或更高級的組件來(subscribe :redux中有 )訂閱應用程序狀態的適當部分。基本上,全部組件都變得聰明。然而,它們是以愚蠢的,聲明性的方式定義的。
@observer class TodoList extends React.Component { render() { const store = this.props.store; return ( <div> { store.report } <ul> { store.todos.map( (todo, idx) => <TodoView todo={ todo } key={ idx } /> ) } </ul> { store.pendingRequests > 0 ? <marquee>Loading...</marquee> : null } <button onClick={ this.onNewTodo }>New Todo</button> <small> (double-click a todo to edit)</small> <RenderCounter /> </div> ); } onNewTodo = () => { this.props.store.addTodo(prompt('Enter a new todo:','coffee plz')); } } @observer class TodoView extends React.Component { render() { const todo = this.props.todo; return ( <li onDoubleClick={ this.onRename }> <input type='checkbox' checked={ todo.completed } onChange={ this.onToggleCompleted } /> { todo.task } { todo.assignee ? <small>{ todo.assignee.name }</small> : null } <RenderCounter /> </li> ); } onToggleCompleted = () => { const todo = this.props.todo; todo.completed = !todo.completed; } onRename = () => { const todo = this.props.todo; todo.task = prompt('Task name', todo.task) || todo.task; } } ReactDOM.render( <TodoList store={ observableTodoStore } />, document.getElementById('reactjs-app') );
下一個清單很好地顯示,咱們只須要改變咱們的數據,而不作任何其餘事情。 MobX將自動從存儲中的狀態從新導出和更新用戶界面的相關部分。
運行結果:
到目前爲止,咱們已經建立了observable對象(原型對象和plain對象),數組和基元(primitives)。你可能想知道,在MobX中如何處理引用?個人state是否容許造成圖表?在上一個列表中,您可能已經注意到todos有一個assignee
屬性。讓咱們給他們一些值,經過引入另外一個「store」(它只是一個glorified數組)包含人,和分配給他們的任務。
那麼問題來了,observableTodoStore.todos原本就是@observable的,奈何有要再增長一個呢?
答:大概由於改變的時候方便?
We now have two independent stores. One with people and one with todos. To assign an assignee
to a person from the people store, we just assigned a reference. These changes will be picked up automatically by the TodoView
. With MobX there is no need to normalize data first and to write selectors to make sure our components will be updated. In fact, it doesn't even matter where the data is stored. As long as objects are made observable, MobX will be able to track them. Real JavaScript references will just work. MobX will track them automatically if they are relevant for a derivation. To test that, just try changing your name in the next input box (make sure you have pressed the above Run code button first!).
咱們如今有兩個獨立的商店。一我的和一個todos。要從people store中分配一個person的代理,咱們只須要分配了一個參考。這些更改將由TodoView自動選取。使用MobX,沒有必要首先規範化數據,而且編寫選擇器以確保咱們的組件將被更新。事實上,數據存儲的位置甚至都不重要。只要對象是可觀察的,MobX將可以跟蹤它們。真正的JavaScript引用將正常工做。 MobX將自動跟蹤它們,若是它們與派生相關。要測試,只需嘗試在下一個輸入框中更改您的名稱(確保您已經按上面的Run代碼按鈕!)。
由於咱們的小Todo應用程序中的一切都是從狀態派生而來的,因此當狀態改變時它並不重要。這使得建立異步操做真的很簡單。只需按下面的按鈕(屢次)來模擬異步加載新的todo項:
後面的代碼真的很簡單。咱們從更新存儲屬性pendingRequests開始,讓UI反映當前的加載狀態。一旦加載完成,咱們更新商店的todos並再次減小pendingRequests計數器。只需將此代碼段與早期的TodoList定義進行比較,便可瞭解如何使用pendingRequests屬性
就這樣!沒有樣板。只是一些UI組件中的簡單的聲明性組件,造成咱們的完整的UI。這些是徹底地,反應性地從咱們的state中獲得。如今,您能夠開始在本身的應用程序中使用mobx和mobx-react包。您到目前爲止學到的東西的簡短摘要:
(我這裏是不行的)
能夠隨意使用上面的可編輯代碼塊玩一段時間,以得到MobX對全部更改的反應的基本感受。例如,您能夠向報告函數添加一條日誌語句,以查看它被調用的時間。或者根本不顯示報告,看看它如何影響TodoList的呈現。或僅在特定狀況下顯示...
人們常用MobX做爲Redux的替代品。但請注意,MobX只是一個庫來解決技術問題,而不是一個架構或甚至狀態容器自己。在這個意義上,上面的例子是設計的,而且建議使用適當的工程實踐,如在方法中封裝邏輯,在商店或控制器等組織它們。或者,正如HackerNews上的某人所說:
「MobX,它在其餘地方被提到,但我不能不讚賞它。在MobX中編寫意味着使用控制器/調度程序/操做/管理程序或另外一種形式的管理數據流返回到一個架構問題,您能夠模式化您的應用程序的須要,而不是默認狀況下須要的任何東西比一個Todo應用程序。