Redux和MobX哪⼀个更适合你的项⽬?
纠正翻译
对于很多 JavaScript 开发⼈员来说,对 Redux 最⼤的抱怨⽆⾮就是需要⼤量实现功能的样板代码。另外⼀个更好的替代品MobX 提供了类似的功能,但是代码量要更少⼀些。
对 MobX 新⼿来说,可以先快速看看 MobX 创始⼈的介绍⽂档。你也可以通过这个教程来获取⼀些实践经验。
本⽂的⽬的是帮助 JavaScript 开发者决定这两个状态管理解决⽅案,到底哪个更适合⾃⼰的项⽬。我已经移植了这个CRUD Redux project到MobX之上来作为本⽂的例⼦。我们将⾸先讨论使⽤ MobX 的利弊,然后再演⽰相同⽰例的两个不同版本来展⽰⼆者的差异。
纠正翻译
本⽂提及的⽰例代码可从 Github 中获取:
Redux CRUD ⽰例
MobX CRUD ⽰例
Redux 和 MobX 有什么共同之处?
⾸先来看看两个框架的共同点,包括:
都是开源库
提供客户端状态管理
⽀持通过redux-devtools-extension实现穿越调试
不依赖于特定的框架
为 React/React Native 框架提供扩展⽀持
使⽤ MobX 的 4 个理由
⾸先来看看 Redux 和 MobX 的主要不同的地⽅。
1. 易学易⽤
对⼀个初学者来说,你可以在 30 分钟内就能学好如何使⽤ MobX。⼀旦你掌握了基础,就不需要学习其他新的。Redux 的基础同样很容易学会。不过,⼀旦你开始构建更复杂的应⽤时,你必须处理这些问题:
同样很容易学会。不过,⼀旦你开始构建更复杂的应⽤时,你必须处理这些问题:
纠正翻译
使⽤redux-thunk处理异步 actions
使⽤redux-saga来简化代码
定义选择器来处理计算值
⽽在 MobX 中,所有的这些情况都被神奇的照顾到,你必须要额外的库来处理这种情况。
2. 编写更少的代码
为了在 Redux 实现⼀个功能,你需要更新⾄少 4 个组件,包含为 reduces、actions、containers 和 components 编写代码。如果你开发的是⼀个⼩项⽬,这会让⼈很恼⽕。MobX 只要求你更新 2 个组件(存储和视图组件)。
纠正翻译
3. ⽀持⾯向对象编程
如果你更喜欢编写⾯向对象代码,那么你对能⽤⾯向对象实现 MobX 的状态管理逻辑会感到很⾼兴。通
过使⽤诸如@observable 和@observer 你可以轻松的开发⾃⼰纯 JavaScript 组件和存储 Reactive。如果你喜欢函数式编程,没问题,这个也⽀持的很好。⽽ Redux 主要侧重于函数式编程的原则。不过你想要⼀个基于类的⽅法,可以使⽤redux-connect-decorator库来实现。
纠正翻译
4. 轻松处理嵌套数据
在⼤多数 JavaScript 应⽤中,你可能会经常需要处理关系或者是嵌套的数据。为了在 Redux 存储中使⽤这些数据,你必须⾸先进⾏规范化。然后需要编写更多代码来管理和跟踪规范化数据的引⽤。
⽽在 MobX,推荐存储中的数据必须是规范化的。MobX 会帮你实现对关系的跟踪并⾃动在更改时重新渲染。通过使⽤域对象来存储数据,你可以直接引⽤在其他存储中定义的域对象。此外,你可以使⽤(@)computed decorators和modifiers for observables来轻松解决复杂数据的要求。
纠正翻译
不使⽤ MobX 的 3 个理由
1. 太过⾃由
Redux 框架提供了⾮常严格的指导⽅针来告诉你如何写状态代码。这意味着你可以轻松编写测试和开发可维护的代码。⽽ MobX 是⼀个没有任何关于实现规则的库。这种⾛捷径⽅式和快速编码的危险就在于编写出来的代码很容易就变得不可维护。
2. 难以调试
MobX 内部的代码⾃动处理了很多让你应⽤变得 Reactive 的逻辑。你的数据在存储和组件之间传递的过程是不可见的,所以当遇见问题时就变得⾮常难以调试。如果你直接在组件中修改状态⽽没有使⽤@actions, 那么你将会很难去确定⼀个错误发⽣的根源。
纠正翻译
3. 应该有⼀个 MobX 更好的替代品
在软件开发中,新兴技术层出不穷。在短短的⼏年内,当前的软件技术已经有过时的势头。⽬前 Redux 和 MobX 已经有很多可以与之竞争的解决⽅案。例如Relay/Apollo & GraphQL, Alt.js和Jumpsuit。这些技术都有潜⼒成为最受欢迎的⼀个。如果你想知道哪个最适合你,你只能是把所有的⽅案都尝试⼀遍。
Redux 和 MobX 的代码⽐较
理论已经讲了很多了,下⾯来看看代码。⾸先我们⽐较⼆者的启动代码。
纠正翻译
引导
Redux 版本:
在 Redux 中我们⾸先定义好存储,然后将之通过 Provider 传递给 App。我们同时需要定义redux-thunk和redux-promise-
在 Redux 中我们⾸先定义好存储,然后将之通过 Provider 传递给 App。我们同时需要定义redux-thunk和redux-promise-middleware来处理异步函数。redux-devtools-extension可以让我们在穿越模式下调试存储。
// src/store.js import { applyMiddleware, createStore } from 'redux'; import thunk from 'redux-thunk'; import promise from 'redux-promise-middleware'; import { composeWithDevTools } from 'redux-devtools-extension'; import rootReducer from './reducers'; const middleware =
react router 以编程方式导航
composeWithDevTools(applyMiddleware(promise(), thunk)); export default createStore(rootReducer, middleware); ------------------------------------------------------------------------------- // src/index.js .... der( <BrowserRouter> <Provider store={store}> <App /> </Provider> </BrowserRouter>,
纠正翻译
MobX 版本:
在 MobX 中我们需要设置多个存储。在这个例⼦我只使⽤⼀个存储,我把它放到⼀个名为allStores 的集合中。然后⽤⼀个Provider将存储集合传递给App。
刚才我们提到 MobX 不需要外部库来处理异步的 actions,所以代码⾏更少⼀些。但是我们需要mobx-remotedev来连接到redux-devtools-extension调试⼯具。
// src/stores/index.js import remotedev from 'mobx-remotedev'; import Store from './store'; const contactConfig = { name:'Contact Store', global: true, onlyActions:true, filters: { whitelist: /fetch|update|create|Event|entity|entities|handleErrors/ } }; const contactStore = new Store('api/contacts'); const allStores = { contactStore: remotedev(contactStore, contactConfig) }; export default allStores; ------------------------------------------------------------------------------- //
src/index.js ... der( <BrowserRouter> <Provider stores={allStores}> <App /> </Provider> </BrowserRouter>,
纠正翻译
这⾥两个版本的代码量⼤致相当。MobX 的 import 语句要更少⼀些。
道具注⼊
Redux 版本:
在 Redux 中,状态和动作通过 react-redux 的 connect() 函数传递给道具。
// src/pages/contact-form-page.js ... // accessing props <ContactForm contact={act} loading={this.props.loading} onSubmit={this.submit} /> ... // function for injecting state into props function mapStateToProps(state) { return { contact: act, errors: s } } // injecting both state and actions into props export default connect(mapStateToProps, { newContact, saveContact, fetchContact, updateContact }) (ContactFormPage);
纠正翻译
MobX 版本:
在 MobX 我们简单的注⼊stores集合。在容器或者组件类的顶部使⽤@inject来实现注⼊过程。这个让stores在props中可⽤,这样我们也可以访问指定的存储并传递给⼦组件。状态和 actions 动作都可以通过 store 对象的属性来访问,因此不需要像 Redux 那样单独进⾏传递。
// src/pages/contact-form-page.js ... @inject('stores') @observer // injecting store into props class ContactFormPage extends Component { ... // accessing store via props const { contactStore:store } = this.props.stores; return ( <ContactForm store={store} form={this.form} contact={ity} /> ) ... }
纠正翻译
MobX 版本看起来更容易理解。但是我们可以使⽤redux-connect-decorators来简化 Redux 代码。在这个地⽅没有明显的谁优谁劣。
定义存储、动作和 Reduces
为了保持⽂章的精简,下⾯是⼀个只有⼀个动作的代码⽰例。
Redux 版本:
在 Redux 中我们需要定义动作和 reducers.
// src/actions/contact-actions.js ... export function fetchContacts(){ return dispatch => { dispatch({ type: 'FETCH_CONTACTS', payload: (url) }) } } ... // src/reducers/contact-reducer ... switch (pe) { case 'FETCH_CONTACTS_FULFILLED': { return { ...state, contacts: action.payload.data.data || action.payload.data, loading: false, errors: {} } } case 'FETCH_CONTACTS_PENDING': { return { ...state, loading: true, errors: {} } } case
'FETCH_CONTACTS_REJECTED': { return { ...state, loading: false, errors: { global: ssage } } } } ...
纠正翻译
MobX 版本:
在 MobX 中 action 和 reducer 的逻辑在⼀个类中就可以完成。我已经在接收到response 后定义了⼀个异步的 action 调⽤另外⼀个action —— entities fetched。
由于 MobX 使⽤ OOP 风格,这⾥定义的Store类已经被重构,可以轻松使⽤类构造器来创建多个存储。所以这⾥的演⽰代码是最基础的,⽽且跟特定的域存储没有做任何绑定。
// src/stores/store.js ... @action fetchAll = async() => { this.loading = true; s = {}; try { const response = await this.service.find({})
runInAction('entities fetched', () => { ities = response.data; this.loading = false; }); } catch(err) { this.handleErrors(err); } } ...
纠正翻译
纠正翻译
信不信与否,两个版本定义的逻辑所做的事情是⼀样的:
更新 UI 的加载状态
异步的获取数据
捕获异常并更新状态
在 Redux 中有 33 ⾏代码,⽽ MobX 只需⼤约 14 ⾏代码就可以完成相同的⼯作。MobX 主要的优势在于你可以在⼏乎所有的域存储类中重⽤基础代码,这个只需少量的修改设置是不做任何修改。这意味着你可以更快的构建应⽤。
其他不同之处
为了在 Redux 中创建表单,我使⽤的是redux-form. ⽽在 MobX ⽤的是mobx-react-form。两个库都很成熟,可以帮你轻松处理业务逻辑。个⼈⽽⾔,我更倾向于mobx-react-form,因为它可以让你使⽤插件来对表单字段进⾏校验。有了redux-form,你也可以编写⾃⼰的校验代码,或者导⼊⼀个校验包来实现这个功能。
纠正翻译
MobX ⼀个⼩⼩的缺陷就是你不能在可观察对象中直接访问特定函数,因为它们不是真正纯 JavaScript 对象。幸运的是它们提供了toJS()函数可以将可观察对象转成 JavaScript 对象。
结论
很明显 MobX的代码要精简得多。通过 OOP 风格和良好的开发实践,你可以快速的构建各种应⽤。最⼤的弊病是很容易编写糟糕的不可维护的代码。
⽽ Redux 更受欢迎⼀些,⽽且特别适合构建⼤型复杂应⽤。这是⼀个规定严格的框架,其规则确保开发⼈员可以编写易于测试和可维护的代码。但是,的确不适合开发⼩项⽬。
纠正翻译
尽管 MobX 有不少缺陷,但如果你遵循良好的习惯仍可以⽤来构建⼤的项⽬。正如爱因斯坦所说的 “ 让⼀切事物尽可能的简单,但不要简单”。
希望我已经提供了⾜够的信息,让你决定是否迁移到 MobX 或者是坚持使⽤ Redux。最终的决定取决于你的项⽬类型以及可⽤的资源。
如果你需要任何 MobX 的⽰例代码,可以在评论⾥说明。我相信你应该会在下⼀个 React 项⽬中试试 MobX 的。