Redux 还是 Mobx?还有没有更好的方式?

我们团队要做一个简小却五脏俱全的小web项目,一致采用 React ,但我们发现 React 经常与 Redux 以及 Mobx 结合使用。于是我们思考,Redux和 Mobx 各有什么优劣?哪个技术选型更适合我们?或者,有没有第三种,更好的方式?因为可能Redux 以及 Mobx都不适合我们现阶段的开发。

太长直接看结论

  1. 先学习 React 组件内部的状态管理功能。在 React 应用,你首先会学到生命周期方法,而且你会用 setState() 和 this.state 解决本地的状态管理。不然你会在 React的生态中迷失。
  2. 在这条学习路径的最后,你会认识到组件内部管理状态难度越来越大。
  3. 这个时候,考虑使用Mobx,它使用小项目,比Redux更易学。但没有Redux规范,Redux用熟后,你更能明白底层机制。Mobx更傻瓜,你不需要理解太多,都帮你做好了。
  4. 随着项目变大,可以考虑Redux,更方便项目质量管理。
  5. React 和 状态管理是解耦的,你可以随时替换Mobx 和 Redux。
  6. 然后,思考更好的方式?

为什么用React?

因为React的选用没有异议,也不是本文的重点。有兴趣可以参见 10 Reasons why I moved from Angular to React

我们要解决的是什么问题?

当我们使用React开发web应用程序时,在React组件内,可以使用this.setState()和this.state处理或访问组件内状态,但是随着项目变大,状态变复杂,通常需要考虑组件间通信问题,主要包括以下两点:

  1. 某一个状态需要在多个组件间共享(访问,更新);
  2. 某组件内交互需要触发其他组件的状态更新;

关于这些问题,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,Jumpsuit,Alt.js等。

状态管理

状态管理库,无论是Redux,还是Mobx这些,其本质都是为了解决状态管理混乱,无法有效同步的问题,它们都支持:

  • 统一维护管理应用状态;
  • 某一状态只有一个可信数据来源(通常命名为store,指状态容器);
  • 操作更新状态方式统一,并且可控(通常以action方式提供更新状态的途径);
  • 支持将store与React组件连接,如react-redux,mobx-react;通常使用状态管理库后,我们将React组件从业务上划分为两类:
    • 容器组件(Container Components):负责处理具体业务和状态数据,将业务或状态处理函数传入展示型组件;
    • 展示型组件(Presentation Components):负责展示视图,视图交互回调内调用传入的处理函数;

Mobx VS Redux

Mobx Redux

在我们深入了解 Redux 和 Mobx 的不同之前,我想先谈谈它们之间的相同之处。

这两个库都是用来管理 JavaScript 应用的状态。它们并不一定要跟 React 绑定在一起,它们也可以在 AngularJs 和 VueJs 这些其他库里使用。但它们与 React 的理念结合得非常好。

如果你选择了其中一个状态管理方案,你不会感到被它锁定了。因为你可以在任何时候切换到另一个解决方案。你可以从 Mobx 换成 Redux 或从 Redux 换成 Mobx。

下面说下不同点

函数式和面向对象

Dan Abramov 的 Redux 是从 flux 架构派生出来的。和 flux 不同的是,Redux 用单一 store 而不是多个 store 来保存 state.

Redux 被 FP(函数式编程)原则所影响。FP 可以在 JavaScript 中使用,但很多人有面向对象语言的背景,比如 Java。他们在刚开始的时候很难适应函数式编程的原则。这就是为什么对于初学者来说 Mobx 可能更加简单。

Michel Weststrate 的 Mobx 则是受到面向对象编程和响应式编程的影响。它将 state 包装成可观察的对象,因此你的 state 就有了 Observable 的所有能力。state 数据可以只有普通的 setter 和 getter,但 observable 让我们能在数据改变的时候得到更新的值。

单一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中可以直接使用新值更新状态对象。

mobx-react和react-redux

使用Redux和React应用连接时,需要使用react-redux提供的Provider和connect:

  1. Provider:负责将Store注入React应用;
  2. connect:负责将store state注入容器组件,并选择特定状态作为容器组件props传递;

对于Mobx而言,同样需要两个步骤:

  1. Provider:使用mobx-react提供的Provider将所有stores注入应用;
  2. 使用inject将特定store注入某组件,store可以传递状态或action;然后使用observer保证组件能响应store中的可观察对象(observable)变更,即store更新,组件视图响应式更新。

代码对比举例

注入Props

Redux :

// src/containers/Company.js
…
class CompanyContainer extends Component {
  componentDidMount () {
    this.props.loadData({});
  }
  render () {
    return <Company
      infos={this.props.infos}
      loading={this.props.loading}
    />
  }
}
…

// function for injecting state into props
const mapStateToProps = (state) => {
  return {
    infos: state.companyStore.infos,
    loading: state.companyStore.loading
  }
}

const mapDispatchToProps = dispatch => {
  return bindActionCreators({
      loadData: loadData
  }, dispatch);
}

// injecting both state and actions into props
export default connect(mapStateToProps, { loadData })(CompanyContainer);

Mobx :

@inject('companyStore')
@observer
class CompanyContainer extends Component {
  componentDidMount () {
    this.props.companyStore.loadData({});
  }
  render () {
    const { infos, loading } = this.props.companyStore;
    return <Company
      infos={infos}
      loading={loading}
    />
  }
}

选择Mobx的原因

  1. 学习成本少:Mobx基础知识很简单,学习了半小时官方文档和示例代码就搭建了新项目实例;而Redux确较繁琐,流程较多,需要配置,创建store,编写reducer,action,如果涉及异步任务,还需要引入redux-thunk或redux-saga编写额外代码,Mobx流程相比就简单很多,并且不需要额外异步处理库;
  2. 面向对象编程:Mobx支持面向对象编程,我们可以使用@observable and @observer,以面向对象编程方式使得JavaScript对象具有响应式能力;而Redux最推荐遵循函数式编程,当然Mobx也支持函数式编程;
  3. 模版代码少:相对于Redux的各种模版代码,如,actionCreater,reducer,saga/thunk等,Mobx则不需要编写这类模板代码;

不选择Mobx的可能原因

  1. 过于自由:Mobx提供的约定及模版代码很少,这导致开发代码编写很自由,如果不做一些约定,比较容易导致团队代码风格不统一,所以当团队成员较多时,确实需要添加一些约定;
  2. 可拓展,可维护性:也许你会担心Mobx能不能适应后期项目发展壮大呢?确实Mobx更适合用在中小型项目中,但这并不表示其不能支撑大型项目,关键在于大型项目通常需要特别注意可拓展性,可维护性,相比而言,规范的Redux更有优势,而Mobx更自由,需要我们自己制定一些规则来确保项目后期拓展,维护难易程度;

推翻前面一切,重新来看

有没有其他的选择?

React 应用广泛使用 Redux 和 Mobx 。但它们是独立的状态管理库,可以运用在除 React 的任何地方。它们的互操作库让我们能简单的连接React 组件。Redux + React 的 react-redux 和 MobX + React 的 mobx-react 。

想使用 Redux 做状态管理的 React 初学者。大部分人认为 React 和 Redux 本身都有颇高的学习曲线,两者结合的话会失控。一个替代的选择就是 Mobx ,因为它更适合初学者。

但是,有位前辈建议, 先学习
React 组件内部的状态管理功能。在 React 应用,你首先会学到生命周期方法,而且你会用 setState() 和 this.state 解决本地的状态管理。我非常推荐上面的学习路径。不然你会在 React 的生态中迷失。在这条学习路径的最后,你会认识到组件内部管理状态难度越来越大。

现在你需要选择其中一个状态管理库。这肯定是要第一时间解决的问题。此外,在开发过相当大的应用之后,你应该能很自如使用 React 。

大部分应用在一开始的时候并不需要大型的状态管理。如若不然,将无法体验 Mobx 和 Redux 这些库所要解决的问题。

初学者用 Redux 还是 Mobx ?

一旦你对 React 组件和它内部的状态管理熟悉了,你就能选择出一个状态管理库来解决你的问题。 Mobx 更适合初学者。我们刚才已经看到 Mobx 只要更少的代码,甚至它可以用一些我们现在还不知道的魔法注解。

用 Mobx 你不需要熟悉函数式编程。像“不可变”之类的术语对你可能依然陌生。函数式编程是不断上升的范式,但对于大部分 JavaScript 开发者来说是新奇的。虽然它有清晰的趋势,但并非所有人都有函数式编程的背景,有面向对象背景的开发者可能会更加容易适应 Mobx 的原则。

随着项目规模增长

在 Mobx 中你改变注解过的对象,组件就会更新。Mobx 比 Redux 使用了更多的内部魔法实现,因此在刚开始的时候只要更少的代码。有 Angular 背景的会觉得跟双向绑定很像。你在一个地方保存 state ,通过注解观察 state ,一旦 state 修改组件会自动的更新。

Mobx 允许直接在组件树上直接修改 state 。

// component
<button onClick={() => store.users.push(user)} />

更好的方式是用 store 的 @action 。

// component
<button onClick={() => store.addUser(user)} />

// store
@action addUser = (user) => {
  this.users.push(user);
}

用 actions 修改 state 更加明确。上面也提到过,有个小功能可以强制的使用 actions 修改 state 。

// root file
import { useStrict } from 'mobx';

useStrict(true);

这样的话第一个例子中直接修改 store 中的 state 就不再起作用了。此外,一旦你只用 actions ,你就已经使用了 Redux 的约束。

在快速启动一个项目时,推荐使用 Mobx ,一旦应用开始变得越来越大,越来越多的人开发时,遵循最佳实践就很有意义,如使用明确的 actions 。这是拥抱 Redux 的约束:你永远不能直接修改 state ,只能使用 actions 。

迁移到 Redux

一旦应用开始变得越来越大,越来越多的人开发时,你应该考虑使用 Redux 。它本身强制使用明确的 actions 修改 state 。action 有 type 和 payload 参数,reducer 可以用来修改 state 。这样的话,一个团队里的开发人员可以很简单的推断 state 的修改。

Redux 提供状态管理的整个架构,并有清晰的约束规则。

另一个 Redux 的优势是在服务端使用。因为我们使用的是纯 JavaScript ,它可以在网络上传输 state 。序列化和反序列化一个 state 对象是直接可用的。当然 Mobx 也是一样可以的。

Mobx 是无主张的,但你可以通过 useStrict() 像 Redux 一样使用清晰的约束规则。这就是我为什么没说你不能在扩张的应用中使用 Mobx ,但 Redux 是有明确的使用方式的。而 Mobx 甚至在文档中说:“ Mobx 不会告诉你如何组织代码,哪里该存储 state 或 怎么处理事件。”所以开发团队首先要确定 state 的管理架构。

状态管理的学习曲线并不是很陡峭。我们总结下建议:React 初学者首先学习恰当的使用 setState() 和 this.state 。一段时间之后你将会意识到在 React 应用中仅仅使用 setState() 管理状态的问题。当你寻找解决方案时,你会在状态管理库 Mobx 或 Redux 的选择上犹豫。应该选哪个呢?由于 Mobx 是无主张的,使用上可以和 setState() 类似,我建议在小项目中尝试。一旦应用开始变得越来越大,越来越多的人开发时,你应该考虑在 Mobx 上实行更多的限制条件或尝试使用 Redux 。我使用两个库都很享受。即使你最后两个都没使用,了解到状态管理的另一种方式也是有意义的。

随时可替换

一旦你选择了一个状态管理库,你会知道那并没有什么限制。它们基本上是和你的应用解耦的,所以是可以替换的。

如何替换并不是本文重点,不再提及。但替换是非常容易。

最后

网上有很多 Redux vs Mobx 的争论,总会有下面这条:“Redux 有太多的样板代码,你应该使用 Mobx,可以减少 xxx 行代码”。这条评论也许是对的,但没人考虑得失,Redux 比 Mobx 更多的样板代码,是因为特定的设计约束。它允许你推断应用状态即使应用规模很大。所以围绕 state 的仪式都是有原因的。

Redux 库非常小,大部分时间你都是在处理纯 JavaScript 对象和数组。它比 Mobx 更接近 vanilla JavaScript 。Mobx 通过包装对象和数组为可观察对象,从而隐藏了大部分的样板代码。它是建立在隐藏抽象之上的。感觉像是出现了魔法,但却很难理解其内在的机制。Redux 则可以简单通过纯 JavaScript 来推断。它使你的应用更简单的测试和调试。

另外,我们重新回到单页应用的最开始来考虑,一系列的单页应用框架和库面临着相同的状态管理问题,它最终被 flux 模式解决了。Redux 是这个模式的成功者。

Mobx 则又处在相反的方向。我们直接修改 state 而没有拥抱函数式编程的好处。对一些开发者来说,这让他们觉得像双向绑定。一段时间之后,由于没有引入类似 Redux 的状态管理库,他们可能又会陷入同样的问题。状态管理分散在各个组件,导致最后一团糟。

使用 Redux,你有一个既定的模式组织代码,而 Mobx 则无主张。但拥抱 Mobx 最佳实践会是明智的。 开发者需要知道如何组织状态管理从而更好的推断它。不然他们就会想要直接在组件中修改它。

两个库都非常棒。Redux 已经非常完善,Mobx 则逐渐成为一个有效的替代。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值