MobX vs Redux - React Conf 2017 纪要

本文首发于知乎 pure render 专栏

26-1

毫无疑问,Redux 与 MobX 是 React 生态中最火热的状态管理工具,社区也一直没有停止对上述两者的讨论。近期,团队小伙伴 黄子毅 的文章 Mobx 思想的实现原理,及与 Redux 对比,以及正在与我一起翻译 MobX 中文文档 岳逢楽 同学的 如果用Redux不爽的话,那就试试MobX吧,都对此发表了自己的观点。在不久前结束的 React Conf 2017 中,Preethi Kasireddy 也做了相关分享,MobX vs Redux: Comparing the Opposing Paradigms,让我们来看看她的观点是怎样的。

状态管理上的异同

Redux / MobX 均为客户端开源状态管理库,用状态来描述 UI 界面,它们与 React 都不具有强绑定关系,你也可以配合别的框架来使用它们。当然,与 React 是再合适不过的了,React 作为 View 层的框架,通过 Virtual DOM 机制来优化 UI 渲染,Redux / MobX 则提供了将相应状态同步到 React 的机制。

26-6

Redux 是 Flux 体系很好的衍生。Flux 具有单向数据流的特点,View 层通过触发注册过 dispatcher 的 action,后由 dispatcher 更新相应的 store。同样 Redux 也具有单向数据流的特点和在 View 层触发 action,但与 Flux 不同的是,Flux 可以具有多个 store,而 Redux store 是单一数据源。Redux 没有 dispatcher 的概念,转而使用纯函数(pure function)代替。Redux store 是不可变的(Immutable)。

26-5

MobX 类似于 Excel 电子表格,你的数据存放在单元格中,是 observable 的。在 MobX 中具有两种 derivation,一种是 computed value,它是纯函数,相当于 Excel 中的公式,依赖于 observable 数据产生。另一种是 reactions,不同于 computed value 产生新的值,它主要处理副作用(side effect),相当于得到 Excel 公式的结果后,把它们显示在屏幕上。

26-4

Redux 与 MobX 的不同主要集中于以下几点:

  • Redux 是单一数据源,而 MobX 往往是多个 store。MobX 可以根据应用的 UI、数据或业务逻辑来组织 store,具体如何进行需要你自己进行权衡。
  • Redux store 使用普通的 JavaScript 对象结构,MobX 将常规 JavaScript 对象包裹,赋予 observable 的能力,通过隐式订阅,自动跟踪 observable 的变化。MobX 是观察引用的,在跟踪函数中(例如:computed value、reactions等等),任何被引用的 observable 的属性都会被记录,一旦引用改变,MobX 将作出反应。注意,不在跟踪函数中的属性将不会被跟踪,在异步中访问的属性也不会被跟踪。
  • Redux 的 state 是只读的,只能通过将之前的 state 与触发的 action 结合,产生新的 state,因此是纯净的(pure)。而 MobX 的 state 即可读又可写,action 是非必须的,可以直接赋值改变,因此是不纯净的(Impure)。
  • Redux 需要你去规范化你的 state,Immutable 数据使 Reducer 在更新时需要将状态树的祖先数据进行复制和更新,新的对象会导致与之 connect 的所有 UI 组件都重复渲染。因此Redux state 不建议进行深层嵌套,或者需要我们在组件中用 shouldComponentUpdate 优化。而 MobX 只自动更新你所关心的,不必担心嵌套带来的重渲染问题。
  • 在 Redux 中区分有 smart 组件与 dumb 组件,dumb 负责展示,smart 负责状态更新,数据获取。而在 MobX 中无需区分,都是 smart,当组件自身依赖的 observable 发生变化时,会作出响应。

两者之争

26-3

学习成本上而言,MobX 是常见的 OO 范式,与 Redux 基于函数式的理论相对比不具有新的概念,对于深入传统面向对象的程序员而言,更为友好。MobX 是 magic 的,通过 computed value、observer class、autorun function 对于 View 层的输入逻辑进行了抽象,我们只需要在 View 层对相应的 observale 数据进行访问即可完成自动变更。Redux 尽管源码简洁,30 分钟即可读完,但一旦开始构建复杂的应用,你就需要开始考虑如何规范化你的 state,如何使用可记忆的 selector 函数(connent 中 mapStateToProps 中对数据的衍生),如何更好地处理异步等等。

代码量而言,MobX 由于内置抽象,需要写的代码比 Redux 少了很多。对于一个简单变更状态的操纵而言,Redux 需要写 action,在 View 层调用 action,根据相应 action type,写对应 reducer,MobX 只需要简单一行即可搞定。

可调试性可预测性可测试性上而言, Redux 均优于 MobX。Redux 是简洁、纯净的,能够进行时间回溯,从 state -> view -> action -> reducer 形成单向闭环,使可调试性和可预测性较好,而对于 action 和 reducer 均可单独测试。而 MobX 内部较复杂,同时为了代码的简洁,可在 View 层直接对 observable 对象进行操作,会使发现问题变得困难。正因如此,MobX 提供了 useStrict 进行限制,只能通过 action 来修改 state。另外 Redux devtool 的完善性和体验也要好于 MobX devtool。

模块化的角度来看,MobX 的 store 是分散的,可以用于封装相应的业务逻辑,Redux 是单一 store,全局共享,没有所谓的模块化,最多只是一些拆分。对于你的组件而言,你需要去共享 state 时,单一数据源便是好的。当你的应用复杂后,对于 MobX 分散 store 间的协调、分享数据、相互引用就会很复杂。

可扩展性可维护性方面,Redux 的所有修改会被集中化处理,严格按照一个接着一个的顺序执行,不需要担心竞态的出现。而 MobX 无法保证严格的顺序执行,改变可以发生在各个地方,随着时间的推移,应用可能会出现维护问题。

而在社区方面,Redux 取得完胜。

如何选择

最初我们是使用 React API setState 来维持组件状态,但伴随应用越来越复杂,内部 state 的管理也变随着复杂,组件间需要共享 state,只能提升到父级。遥远的叶子节点需要 state,必须从父级逐层传递,中间的组件将出现 props 冗余。这时就需要 Redux / MobX 将组件内部的状态都抽离出来,做统一管理。

Preethi Kasireddy 认为需要快速开发简单应用的小团队可以考虑使用 MobX,因为 MobX 需要开发的代码量小,学习成本低,上手快,适合于实时系统,仪表盘,文本编辑器,演示软件,但不适用于基于事件的系统。

而 Redux 适用于大团队开发复杂应用,Redux 在可扩展性和可维护性方面可以 hold 住多人协作与业务需求多变,适合商业系统、基于事件的系统以及涉及复杂反应的游戏场景。

Redux 对 action / event 作出反应,而 MobX 对 state 变化作出反应。在更新逻辑方面,MobX 是在 store 内进行相应封装,假设一个 UI 操作需要更新多个 store 内的值时,那么我们需要多次触发对应 action,或者在多个 store 上再添加一个 store 去进行控制,这都不如 Redux 来得优雅,Redux 只需要触发一个 action,多个 reducer 可响应相同的 action type,即可完成更新。

26-2

最后,Preethi Kasireddy 结合了 Redux 的优势(纯净的 reducer 与 Immutable store)与 MobX 的优势(响应式),提出是否有 reducer 与 observable 相结合的可能,很有意思。

后记

在我看来,Redux 崇尚自由,小而美,在框架层面给予了开发者很大的空间,比如可以利用 middleware 机制去抽象业务逻辑。激发了大家造轮子的热情的同时,也引发了“全家桶”的吐槽,光异步请求就有 thunk、saga 等多种解决方案,令后来的开发者眼花缭乱。

MobX 则相对封闭,性能优势与代码量小,无法掩盖最佳实践的缺失。store 应该如何更好地组织?store 间通讯应该如何较好地处理?如何去做一些通用的抽象,比如 store 的复用?都需要我们去一一解决。

正如 Preethi Kasireddy 在演讲最后提到的,Redux 或是 MobX 的选择并不会意味着结束,或者说在现阶段下二者都有欠缺。一方面,我们可以通过封装 Redux 去降低开发成本,来适用自己的业务场景,提高工程效率,比如蚂蚁的dva,或是我所在团队即将开源的框架。亦或我们可以取长补短,MobX 胜在响应式带来的性能提高,避免不必要的重渲染,如果能与 Redux 的 middleware、reducer 结合,将会非常值得期待。