简要介绍:最近看了一下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。