本文作者:IMWeb jerytang 原文出处:IMWeb社区 未经同意,禁止转载
mobx 简介
和 redux 类似,mobx 是一个数据管理库,都可以和 react 配合使用。它区别于 redux 的最大特点是,可以直接修改数据,对 UI 进行精确刷新。精确更新是什么意思呢,看下面的例子。
代码语言:javascript复制import { observable } from 'mobx';
const obj = observable({
a: 1,
b: 2
})
autoRun(() => {
console.log(obj.a)
})
obj.b = 3 // 什么都没有发生
obj.a = 2 // observe 函数的回调触发了,控制台输出:2
- autoRun 中的函数对 a 进行了取值
get
操作,obj.a
和所在的函数完成了绑定关系; - 直接对
obj.a
进行赋值set
操作,触发了get
操作所在的函数执行; - 对 b 的操作没有触发——mobx 是精确到字段更新
将 mobx 的数据管理能力应用到 react 中: React Component 对数据源字段进行精确响应更新。
mobx 的更多介绍,移步mobxjs官网。
这里直接上实际例子。
mobx 的例子是辅导题库项目中实践方案的简化演示。
实际例子:mobx 和 redux 对比
实现一个计数器增加、减少的功能。
如果直接使用 setState
也很容易实现这个功能。但是,这里分别用redux方案 和 mobx方案 实现上面的功能。
为了演示方便,将所有的代码都放在一个文件中。查看 mobx 实现的代码前,先了解下装饰器(decorator)是什么。
redux 方案代码
代码语言:javascript复制import React, { Component } from 'react';
import {
createStore,
bindActionCreators,
} from 'redux';
import { Provider, connect } from 'react-redux';
// ①action types
const COUNTER_ADD = 'counter_add';
const COUNTER_DEC = 'counter_dec';
const initialState = {a: 0};
// ②reducers
function reducers(state = initialState, action) {
switch (action.type) {
case COUNTER_ADD:
return {...state, a: state.a 1};
case COUNTER_DEC:
return {...state, a: state.a-1};
default:
return state
}
}
// ③action creator
const incA = () => ({ type: COUNTER_ADD });
const decA = () => ({ type: COUNTER_DEC });
const Actions = {incA, decA};
class Demo extends Component {
render() {
const { store, actions } = this.props;
return (
<div>
<p>a = {store.a}</p>
<p>
<button className="ui-btn" onClick={actions.incA}>增加 a</button>
<button className="ui-btn" onClick={actions.decA}>减少 a</button>
</p>
</div>
);
}
}
// ④将state、actions 映射到组件 props
const mapStateToProps = state => ({store: state});
const mapDispatchToProps = dispatch => ({
// ⑤bindActionCreators 简化 dispatch
actions: bindActionCreators(Actions, dispatch)
})
// ⑥connect产生容器组件
const Root = connect(
mapStateToProps,
mapDispatchToProps
)(Demo)
const store = createStore(reducers)
export default class App extends Component {
render() {
return (
<Provider store={store}>
<Root />
</Provider>
)
}
}
mobx 方案代码
代码语言:javascript复制import React, { Component } from 'react';
import { observable, action } from 'mobx';
import { Provider, observer, inject } from 'mobx-react';
// 定义数据结构
class Store {
// ① 使用 observable decorator
@observable a = 0;
}
// 定义对数据的操作
class Actions {
constructor({store}) {
this.store = store;
}
// ② 使用 action decorator
@action
incA = () => {
this.store.a ;
}
@action
decA = () => {
this.store.a--;
}
}
// ③实例化单一数据源
const store = new Store();
// ④实例化 actions,并且和 store 进行关联
const actions = new Actions({store});
// inject 向业务组件注入 store,actions,和 Provider 配合使用
// ⑤ 使用 inject decorator 和 observer decorator
@inject('store', 'actions')
@observer
class Demo extends Component {
render() {
const { store, actions } = this.props;
return (
<div>
<p>a = {store.a}</p>
<p>
<button className="ui-btn" onClick={actions.incA}>增加 a</button>
<button className="ui-btn" onClick={actions.decA}>减少 a</button>
</p>
</div>
);
}
}
class App extends Component {
render() {
// ⑥使用Provider 在被 inject 的子组件里,可以通过 props.store props.actions 访问
return (
<Provider store={store} actions={actions}>
<Demo />
</Provider>
)
}
}
export default App;
使用 mobx 时,借鉴了 redux 架构的优点:
- 单一数据源,这样避免了子组件、父组件状态同步的问题
- 可以做到让组件无状态化
- 使用 Provider 注入,让
store
actions
可以在子组件中,通过 props 访问使用
下面是一些不同点:
- mobx 使用的是
@inject
装饰器语法注入,redux 使用的是connect
语法注入 - mobx 使用
@observer
语法,让一个 component 能响应store
字段更新 - mobx 会动态精确绑定数据字段和对应
component
关系, redux 使用connect
参数手动控制传递哪些字段 - mobx 直接修改 store 的状态,但是必须在
@action
修饰的函数中完成,@action
的语义,表示这是一个修改状态的操作 - redux Provider 传递
store
是强约定,mobx Provider 灵活传递store
actions
,也可以是其它名字,比如db
- redux 使用了比较难以理解的高阶函数和参数
connect
combineReducers
bindActionCreators
mapStateToProps
mapDispatchToProps
,mobx 方案,除了使用 decorator 语法,没有其它让人感觉理解困难的函数。 - redux 引入了数据流,mobx 没有数据流的概念,通过 actions 直接改变数据
编码工作量对比
代码功能少,感觉不到差别,好像就是 redux 方案有点难理解;而 mobx 比较直接,也比较 magic,就像当年刚开始使用 jQuery 的感觉。
现在给计数器增加乘以 2 倍的功能。
看下 两个方案的代码修改量差异
redux
mobx
两者都要在界面上做调整。
- redux 方案需要要改 3 个地方: action_types、action_creator 、reducer
- mobx 方案需要改 1 个地方: 添加一个 action
实现同样功能,redux 需要关注的地方多了 2/3。功能比较少的时候,感觉不到工作量差异多大。但是如果有个 500 个 action 要处理,这时候工作量的差距就是按时完成和加班也做不完的差别了。
这里是增加功能,同样,删除功能,也要删除更多的地方,改动更多的文件。
在实际项目中,action_types、action_creator 、reducer 分布于不同的文件夹,需要来回切换文件修改。
随着功能逐渐增加,redxu 方案,用一个 reducer 来处理,可能就不合适了,需要对 reducers 进行了拆分;mobx 方案也面临类似的问题,Actions、Store 类会越来越大。
如何扩展呢?
mobx 在大项目中的扩展能力
redux 方案,本质上还是通过添加更多的 switch
语句来实现扩展,将 store 分支节点的 reducer 分散到不同的文件,再通过工具函数combineReducers
合并成一个 rootReducer。
mobx 方案的扩展非常简单,需要扩展 store 和 actions。并且,actions 和 store 的扩展方式完全一致,通过给父类添加成员:
结论
为了不加班,我站 mobx 这边。
参考
- Mobx 思想的实现原理