簡要介紹:最近看了一下Mobx,然後有一篇入門教程是英文的,這裏翻譯一下並寫寫自己的心得體會。
原文地址:https://mobx.js.org/getting-started.html
一、Mobx的核心思想
(1)Mobx的核心思想概括:
貫穿mobx狀態管理機的核心思想是使得state保持一致性,我是這麼理解
的,任何來源於state的數據,顯示都應該與state的改變保持一致性,換
句話說,只要state改變,那麼顯示的view層就會自動發生改變。
(2)Mobx中的數據流
簡單來說,view層的渲染或者更新直接來源於state,而actions可以直接改變state,對比與redux的數據流,我們發現少了reducer這個過程,因此mobx較爲簡單易用。
–state,聲明被監聽的數據結構,這裏的數據結構可以是對象,數組,原
始類型或者是引用類型,並且state是整個應用的數據來源”data cell”
–來源於state的衍生物,基礎的衍生物是計算屬性(computed),計算
屬性可以來源於一些簡單的值,或者是一些運算等等,換種話說,計算屬
性可以是方程或者是你應用中的表格,用於展示數據。
–來源於state的另一個衍生物是反應(reaction),與計算屬性不同的是
reaction不會返回一個結果值,它用於執行一些工程或者說函數,比如一
些I/O操作,或者網絡請求等等。
–最後就是動作(action),動作是用於改變state的,mobx的特點是,保
證了通過action改變了action後,會自動的反應到衍生物reaction和
computed,從而達到一個自動更新View的目的。
二、 一個簡單的 No MobX store
我們簡單的來說一下沒有采用mobx的store例子,從一個todo store看起:
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();
這裏定義了一個TodoStore類,類的構造成員爲todo數組,可以添加數組addTodo,report方法,可以輸出數組的長度,以及數組中有多少個元素的completed爲true。
下面來看手動執行這幾個構造方法的過程:
todoStore.addTodo("read MobX tutorial");
console.log(todoStore.report());
todoStore.addTodo("try MobX");
console.log(todoStore.report());
todoStore.todos[0].completed = true;
console.log(todoStore.report());
todoStore.todos[1].task = "try MobX in own project";
console.log(todoStore.report());
todoStore.todos[0].task = "grok MobX tutorial";
console.log(todoStore.report());
執行的結果爲:
Next todo: "read MobX tutorial". Progress: 0/1
Next todo: "read MobX tutorial". Progress: 0/2
Next todo: "read MobX tutorial". Progress: 1/2
Next todo: "read MobX tutorial". Progress: 1/2
Next todo: "grok MobX tutorial". Progress: 1/2
存在的問題:
就是每次添加完元素,或者改變了元素的completed屬性之後,都必須手
動的執行todoStore.report()方法才能輸出結果。也就是說不能自動的監
聽成員屬性todo數組的變化。
三、通過mobx使得todo store自動響應
我們看到了上述例子的缺陷就是不能自動執行響應,我們這裏把todo數
組看成是數據源,而report()函數看成是響應函數,也就是我們需要實現
讓state變動的時候自動執行響應,我們可以利用mobx。
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();
上述代碼就實現了一個state改變後自動執行的ObservableTodoStore
類,這裏我們通過mobx提供的observable裝飾符,監聽todos數據,
根據mobx提供的computed計算屬性,這個計算屬性會保證在todos觀
測屬性改變的時候自動執行。下面我們來看執行方法和結果。
執行方法:
observableTodoStore.addTodo("read MobX tutorial");
observableTodoStore.addTodo("try MobX");
observableTodoStore.todos[0].completed = true;
observableTodoStore.todos[1].task = "try MobX in own project";
observableTodoStore.todos[0].task = "grok MobX tutorial";
執行結果:
Next todo: "read MobX tutorial". Progress: 0/1
Next todo: "read MobX tutorial". Progress: 0/2
Next todo: "read MobX tutorial". Progress: 1/2
Next todo: "read MobX tutorial". Progress: 1/2
Next todo: "grok MobX tutorial". Progress: 1/2
我們發現了有趣的事情,通過mobx實現了監聽和自執行之後,我們在添
加元素或者改變todos元素的值得時候,會自動的調用computed修飾的
函數,實現了state改變,自動執行computed的效果。
四、在react中利用mobx
在react中利用mobx本質,同上述的例子是一樣的,只不過通過
observer修飾了react組件之後,render方法變成了一個監聽函數,
一旦store發生改變,就會改重新去render,store通過react的context來
傳遞到所有子組件。實現代碼如下:
@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')
);
改變store後的方法:
const store = observableTodoStore;
//observableTodoStore=new ObservableTodoStore();
store.todos[0].completed = !store.todos[0].completed;
store.todos[1].task = "Random todo " + Math.random();
store.todos.push({ task: "Find a fine cheese", completed: true });
因爲render有todos屬性,只要todos在外界也就是傳入的store中發生改
變,都會重新render。