blog
blog copied to clipboard
MobX 入门教程
大家用 redux 这么久,有没有被那么多概念和约定烦到?
比如:
- 一个 click 的事件需要经过 action, dispatch, middleware, reducer 才能走完流程
- reducer 里不能直接修改 state,而是每次返回一个新的
- 要区分 container 和 component
- container 里要在 connect 里 select 数据,一不小心就选多了或选少了,出于性能考虑还要借助 reselect 这个库
- 发异步请求需要借助 redux-thunk 或 redux-saga
- ...
如果有,那么可以尝试下 mobx 。
什么是 mobx
mobx 只做一件事,解决 state 到 view 的数据更新问题。

mobx 是一个库 (library),不是一个框架 (framework)。他不限制如何组织代码,在哪里保存 state 、如何处理事件,怎么发异步请求等等。我们可以回归到 Vanilla JavaScript,可以和任意类库组合使用。
核心理念
mobx 引入了几个概念,Observable state, Derivations 和 Reactions。

可以拿 Excel 表格做个比喻,Observable state 是单元格,Derivations 是计算公式,单元格的修改会触发公司的重新计算,并返回值,而最终公式的计算结果需要显示在屏幕上(比如通过图表的方式),这是 Reactions。
下面通过代码理解下这些概念,以 mobx 和 react 的组合使用为例:(Open Demo on jsfiddle)
import { observable, computed } from 'mobx';
import { observer } from 'mobx-react';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
////////////////////
// Store
class TodoStore {
@observable todos = [];
@computed get completedTodosCount() {
return this.todos.filter(todo => todo.completed === true).length;
}
addTodo(task) {
this.todos.push({ task, completed: false });
}
}
////////////////////
// Components
@observer
class TodoList extends Component {
render() {
const { todoStore } = this.props;
return (
<div>
{ todoStore.todos.map((todo, index) => <Todo todo={todo} key={index} />) }
Progress: { todoStore.completedTodosCount }
</div>
);
}
}
@observer
class Todo extends Component {
render() {
const { todo } = this.props;
return (
<li onDoubleClick={this.onRename}>
<input
type="checkbox"
checked={ todo.completed }
onChange={ this.onToggleCompleted }
/>
{ todo.task }
</li>
);
}
onToggleCompleted = () => {
const todo = this.props.todo;
todo.completed = !todo.completed;
}
onRename = () => {
const todo = this.props.todo;
todo.task = prompt('Task name', todo.task) || "";
}
}
////////////////////
// Init
const todoStore = new TodoStore();
todoStore.addTodo('foo');
todoStore.addTodo('bar');
ReactDOM.render(
<TodoList todoStore={todoStore} />,
document.getElementById('mount')
);
这里通过 @observable 定义 Observable state,通过 @computed 定义 Derivations,通过 @observer 封装了 React Component 的 render 方法,这是 Reactions。
为什么用 mobx
mobx 官网 罗列了不少区分与 flux 框架的优点,这里摘录一些比较打动我的。
简单
没有 connect,没有 cursor,没有 Immutable Data ... 总之感觉会少很多代码。同时概念也更少。
可以用 class 来组织和修改数据
对于组织复杂的领域模型比较适用。
可以用 JavaScript 引用来组织和修改数据
比如,可以直接
todo.completed = true;而不需要
return todos.map((todo) => { if (todo.id === action.payload.id) { return {...todo, {completed: true}} else { return todo; } });同时也不需要引入额外的 immutable.js。
性能相比 redux 有优势
mobx 会建立虚拟推导图 (virtual derivation graph),保证最少的推导依赖。dan_abramov 亲自操刀为 todoMVC 做了极致的优化才和 mobx 打成平手。链接
不足
参考之前 redux + redux-saga 的方案,这里的一些点可能会成为你我不用他的原因。
浏览器兼容性,不支持 IE8
由于用了 reactive arrays, objects with reactive properties (getters) 这些 ES5 特性,而且这些特性不能通过 es5-shim 解决。兼容列表可参考:http://kangax.github.io/compat-table/es5/
缺少最佳实践
这部分不在 mobx 的范围之内,需要自己探索一套最佳实践。比如如何触发 action,如何组织 store,如何组织业务逻辑,如何发异步请求,如何在 React Component 之间传递数据等等。
热替换 (Hot Module Replacement)
用过 HMR,就不愿再回到手动刷页面的时代。mobx 支持 [react-transform] 的热替换方式,但是否支持 webpack 原生热替换情况下对 store 进行替换,还有待探索。
总结
mobx 简单高效,在用吐了 redux 之后,对 mobx 简直爱不释手。除了不支持 IE8 这个硬伤,其他缺点都还是可以接受的。我会在后面的小项目中应用它,并尝试探索一套最佳实践。
扩展阅读
简单的用用,觉得真是无比清晰,基本把store的处理,简化为对model的处理。这种情况下,测试的编写也变的异常简单。
比较纠结的就是store的组织。redux就是单store,一棵state树。用mobx,很自然的有多个model,这些model要不要最后合成一个store,然后通过最上层的组件注入,还是每个组件拥有自己的domain store,app级的store再通过上层传入。
其实写redux也有类似的困惑,比如<UserList /> ,本身就与一个User数组对应,我直接扔进去,复用性是最好的,但是用redux,要通过最上层的store传下来。
现在能想到的就是,写个高阶函数,hoc(components,store)返回绑定好了的 <UserListWrapper/>,这个store根据业务场景,看是否需要多个组件公用。react-router里使用这个wrapper。
楼主有使用mobx写点稍大项目的经验吗,期待经验分享
热更新是支持的
试用了一下,Vue即视感。其实既然这样,我完全可以考虑用Vue的,现在2.0性能也蛮不错的感觉
react想要支持IE8也需要自己完全编译react和redux相关代码,无法利用公共CDN里面的预编译的js
react 要支持IE8 有一个库的
写了一篇 Mobx 思想的实现细节,希望能对此进行补充:https://github.com/ascoders/blog/issues/16 。
至于 vue 既视感,要看未来浏览器对其语法支持的程度,目前 magic 逻辑太多,大型项目可能不便于维护。
可以拿 Excel 表格做个比喻,Observable state 是单元格,Derivations 是计算公式,单元格的修改会触发公司的重新计算
这是公司 --> 公式
可以学习一下
mark
请问, 在用inject的前提下, 有react-hot-loader热更新store的解决方案吗?
@daiyunchao 请问你是怎么解决store热更新的
期待在忙完umi的空余时间写一下 mbox最佳实践
期待下一篇关于mobx的最佳实践
同样期待能有一篇最佳实践。