在过去一年,公司使用的React状态管理库一直都是Mobx,由于历史原因,在后续重构版本也并没有去更换它,团队也相对熟悉它,用起来也还算比较舒适,相比redux不用编写大量的模板代码,对比大多开发者来说,redux并不是不会用,而是它比较繁琐(可能Mobx作者就是这么认为的)
前言
当我们在使用React开发大型程序的时候,随着项目变大,状态变复杂,我们通常要考虑通信的问题,主要包括如状态在多个组件内需要共享(不单单是父子组件等),某组件内的交互需要触发其它组件更新,关于这些问题,React组件开发实践推荐将公用组件状态提升:
Often, several components need to reflect the same changing data. We recommend lifting the shared state up to their closest common ancestor 通常多组件需要处理同一状态,我们推荐将共享状态提升至他们的共同最近祖先组件内。更多详情查看
但是随着项目的发展,我们会发现状态提升已经不能满足我们的项目了,于是我们就会引入一些状态管理库,如redux、mobx等
Mobx介绍
Mobx是一个透明函数响应式编程(Transparently Functional Reactive Programming,TFRP)的状态管理库,它使得状态管理简单可伸缩:
Anything that can be derived from the application state, should be derived. Automatically. 任何起源于应用状态的数据应该自动获取
核心概念
- observable: 可观察的状态,意味着mobx能够监测到这个数据的改动,并可以对它做出反应
- computed:计算数据,类似Vue中的computed
- action: 动作,修改状态。
apis
observable
const name = observable({ name: thl })
makeObservable
用在class state中,能够识别哪些属性是observable或者computed,哪些方法需要支持action或者是flow
constructor() {
makeObservable(this, {
value: observable,
double: computed,
increment: action,
fetch: flow
})
}
makeAutoObservable
makeObservable的智能版,能够自动推断所有属性,规则如下:
- 所有 自有 属性都成为
observable。 - 所有
getters都成为computed。 - 所有
setters都成为action。 这里有必要提到一般我们mobx相对常用的实践
class MyStore {
constructor() {
makeAutoObserble(this)
}
name = thl,
setName(name) {
this.name = name
}
}
export default new MyStore()
useObserver
import { useObserver, useLocalStore } from 'mobx-react' // 6.x or mobx-react-lite@1.4.0`
function Person() {
const person = useLocalStore(() => ({ name: 'wsy' }))
return useObserver(() => (
<div>
{person.name}
<button onClick={() => (person.name = 'wsy')}>No! I am Mike</button>
</div>
))
}
export default Person
observer
这是一个高阶组件,相比于例如我们在jsx以外使用到了可观察的状态,但是希望组件更新,那我们可以使用这个高阶组件
import { observer, useLocalStore } from 'mobx-react' // 6.x or mobx-react-lite@1.4.0
export const Counter = observer<Props>(props => {
const store = useLocalStore(() => ({
count: props.initialCount,
inc() {
store.count += 1
},
}))
return (
<div>
<span>{store.count}</span>
<button onClick={store.inc}>Increment</button>
</div>
)
})
Mobx相比Redux的区别
函数式和面向对象
Redux更多的是遵循函数式编程(Functional Programming, FP)思想,而Mobx则更多从面相对象角度考虑问题。
Redux提倡编写函数式代码,如reducer就是一个纯函数(pure function),如下:
(state, action) => {
return Object.assign({}, state, {
...
})
}
纯函数,接受输入,然后输出结果,除此之外不会有任何影响,也包括不会影响接收的参数;对于相同的输入总是输出相同的结果。
Mobx设计更多偏向于面向对象编程(OOP)和响应式编程(Reactive Programming),通常将状态包装成可观察对象,于是我们就可以使用可观察对象的所有能力,一旦状态对象变更,就能自动获得更新。
单一store和多store
store是应用管理数据的地方,在Redux应用中,我们总是将所有共享的应用数据集中在一个大的store中,而Mobx则通常按模块将应用状态划分,在多个独立的store中管理。
JavaScript对象和可观察对象
Redux默认以JavaScript原生对象形式存储数据,而Mobx使用可观察对象:
- Redux需要手动追踪所有状态对象的变更;
- Mobx中可以监听可观察对象,当其变更时将自动触发监听;
不可变(Immutable)和可变(Mutable)
Redux状态对象通常是不可变的(Immutable):
switch (action.type) {
case REQUEST_POST:
return Object.assign({}, state, {
post: action.payload.post
});
default:
retur nstate;
}
我们不能直接操作状态对象,而总是在原来状态对象基础上返回一个新的状态对象,这样就能很方便的返回应用上一状态;而Mobx中可以直接使用新值更新状态对象。
Mobx4和Mobx6
mobx4 的问题
- 需要写 @observable @action @action.bound 代码。
- 不能定义空对象
- 数据格式问题。 数组不是数组等 关于不是数组的问题,
Array.isArray(observable([])); //false
Mobx4及以下版本的observableArray是一个array-like object,但是与我们通常理解的array-like object(如arguments对象)又不一样。Mobx4及以下版本的observableArray的特点是:
- ECMAScript Array的方法observableArray也可以使用
- .sort()和.reverse()方法并不会改变原来的Array,而是返回一个操作后的copy。
- 如果你需要一个原生的Array(比如在调用第三方库的时候,需要一个原生的Array),需要使用.peek()或者.slice()方法
mobx6 的收益
- 以上问题都解决了。
- 写法非常的简洁,仅需要在 constructor 里调用 makeAutoObservable 即可
- Proxy
总结
mobx特别是6之后用起来是比较舒适的,本来想顺便分享点源码,但是代码down下来之后一时半会没有理清楚,希望以后有时间可以阅读其源码。