mobx redux
对于许多JavaScript开发人员而言,Redux的最大抱怨是实现功能所需的样板代码数量。 更好的替代方法是MobX,它提供类似的功能,但编写的代码较少。
对于MobX的新手,请快速浏览一下MobX的创建者编写的介绍 。 您也可以通过本教程来获得一些实践经验。
本文的目的是帮助JavaScript开发人员确定这两种状态管理解决方案中的哪一种最适合其项目。 我已将此CRUD Redux项目迁移到MobX,以用作本文的示例。 我将首先讨论使用MobX的利弊,然后我将演示两种版本的实际代码示例,以说明两者之间的区别。
可以在GitHub上找到本文提到的项目的代码:
如果您喜欢这篇文章,那么您可能还想注册SitePoint Premium,并观看我们使用React和Redux处理表单的课程。
Redux和MobX有什么共同点?
首先,让我们看一下两者的共同点。 他们:
- 是开源库
- 提供客户端状态管理
- 通过redux-devtools-extension支持时间旅行调试
- 不受特定框架约束
- 对React / React Native框架有广泛的支持。
使用MobX的4个理由
现在,让我们看一下Redux和MobX之间的主要区别。
1.易于学习和使用
对于初学者,您可以在30分钟内学习如何使用MobX。 一旦学习了基础知识,就是这样。 您无需学习任何新知识。 使用Redux,基础也很容易。 但是,一旦开始构建更复杂的应用程序,就必须处理:
- 使用redux-thunk处理异步操作
- 使用redux-saga简化代码
- 定义选择器以处理计算值等
使用MobX,可以“神奇地”处理所有这些情况。 您不需要其他库来处理这种情况。
2.减少编写代码
要在Redux中实现功能,您至少需要更新四个工件。 这包括编写用于化简器,操作,容器和组件的代码。 如果您正在做一个小型项目,这会特别烦人。 MobX仅要求您至少更新两个工件(即商店和视图组件)。
3.全面支持面向对象的编程
如果您喜欢编写面向对象的代码,将很高兴知道您可以使用OOP通过MobX来实现状态管理逻辑。 通过使用@observable
和@observer
之类的装饰器,您可以轻松地使普通JavaScript组件和存储具有响应性。 如果您更喜欢函数式编程,那没问题-也支持。 另一方面,Redux在很大程度上致力于功能编程原则。 但是,如果需要基于类的方法,则可以使用redux-connect-decorator库。
4.处理嵌套数据很容易
在大多数JavaScript应用程序中,您会发现自己正在处理关系数据或嵌套数据。 为了能够在Redux存储中使用它,您必须首先对其进行规范化 。 接下来,您必须编写更多代码来管理对规范化数据中引用的跟踪。
在MobX中, 建议以非规范化形式存储数据。 MobX可以为您跟踪关系,并将自动重新呈现更改。 通过使用域对象存储数据,您可以直接引用其他存储中定义的其他域对象。 另外,您可以对观察值使用(@)计算的修饰符和修饰符,以轻松解决复杂的数据挑战。
不使用MobX的3个理由
1.太多的自由
Redux是一个框架,提供了有关如何编写状态代码的严格准则。 这意味着您可以轻松编写测试并开发可维护的代码。 MobX是一个库,没有关于如何实现它的规则。 这样做的危险在于,采用快捷方式并应用快速修复程序非常容易,这可能导致无法维护的代码。
2.难以调试
MobX的内部代码“神奇地”处理了许多逻辑,使您的应用程序具有响应性。 在存储区和组件之间有一个不可见的区域,您的数据在其中传递,这使您在遇到问题时难以调试。 如果直接在组件中更改状态,而不使用@actions
,那么将很难确定错误的来源。
3.可能有更好的替代MobX
在软件开发中,不断出现新的趋势。 在短短几年内,当前的软件技术会Swift失去动力。 目前,有几种解决方案与Redux和Mobx竞争。 有几个例子是继电器/阿波罗和GraphQL , Alt.js和连衣 。 这些技术中的任何一种都有可能成为最受欢迎的技术。 如果您真的想知道哪一个最适合您,则必须全部尝试。
代码比较:Redux vs MobX
理论足够多,让我们看一下代码。 首先,我们比较每个版本的引导方式。
自举
Redux版本:
在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 = composeWithDevTools(applyMiddleware(promise(), thunk));
export default createStore(rootReducer, middleware);
-------------------------------------------------------------------------------
// src/index.js
…
ReactDOM.render(
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>,
document.getElementById('root')
);
免费学习PHP!
全面介绍PHP和MySQL,从而实现服务器端编程的飞跃。
原价$ 11.95 您的完全免费
MobX版本:
在MobX中,我们需要设置多个商店。 在这种情况下,我仅使用一个商店,将其放置在名为allStores
的集合中。 然后使用Provider
将商店集合传递给App
。
如前所述,MobX不需要外部库来处理异步操作,因此行数更少。 但是,我们确实需要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
…
ReactDOM.render(
<BrowserRouter>
<Provider stores={allStores}>
<App />
</Provider>
</BrowserRouter>,
document.getElementById('root')
);
这两个版本中的代码量大致相同。 MobX的导入语句较少。
道具注射
Redux版本:
在Redux中,使用react-redux的connect()
函数connect()
状态和动作传递给prop。
// src/pages/contact-form-page.js
…
// accessing props
<ContactForm
contact={this.props.contact}
loading={this.props.loading}
onSubmit={this.submit}
/>
…
// function for injecting state into props
function mapStateToProps(state) {
return {
contact: state.contactStore.contact,
errors: state.contactStore.errors
}
}
// injecting both state and actions into props
export default connect(mapStateToProps, { newContact,
saveContact,
fetchContact,
updateContact
})(ContactFormPage);
MobX版本:
在MobX中,我们只需注入stores
集合。 我们在容器或组件类的顶部使用@inject
来执行此操作。 这使得stores
可以在props
,从而使我们能够访问特定商店,并将其传递给子组件。 状态和动作都可以通过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={store.entity}
/>
)
…
}
MobX版本似乎更易于阅读。 但是,我们可以使用redux-connect-decorators简化Redux代码。 在这种情况下,不会有明确的赢家。
定义商店,动作和减少者
为了使本文简洁,我将向您展示一个动作的代码示例。
Redux版本:
在Redux中,我们需要定义操作和减少器。
// src/actions/contact-actions.js
…
export function fetchContacts(){
return dispatch => {
dispatch({
type: 'FETCH_CONTACTS',
payload: client.get(url)
})
}
}
…
// src/reducers/contact-reducer
…
switch (action.type) {
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: action.payload.message }
}
}
}
…
MobX版本:
在MobX中,动作和减速器的逻辑是在一类中完成的。 我定义了一个异步动作,该动作调用在收到response
后entities fetched
另一个动作entities fetched
。
由于MobX使用OOP样式,因此对此处定义的Store
类进行了重构,以允许使用类构造函数轻松创建多个商店。 因此,此处演示的代码是与特定域存储无关的基本代码。
// src/stores/store.js
…
@action
fetchAll = async() => {
this.loading = true;
this.errors = {};
try {
const response = await this.service.find({})
runInAction('entities fetched', () => {
this.entities = 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有很多缺点,但如果遵循良好的做法,您仍然可以构建大型项目。 用爱因斯坦(Albert Einstein)的话来说,“使一切尽可能简单,但不要简单”。
我希望我提供了足够的信息来明确说明是否要迁移到MobX还是坚持使用Redux。 最终,决定取决于您正在处理的项目类型以及可用资源。
本文由Dominic Myers和Vildan Softic进行了同行评审。 感谢所有SitePoint的同行评审员使SitePoint内容达到最佳状态!
如果您想使用Redux游戏,请注册SitePoint Premium并注册我们的课程Redux Design Issues and Testing 。 在本课程中,您将构建一个Redux应用程序,该应用程序通过Websocket连接接收按主题组织的推文。 要让您品尝一下商店中的商品,请查看下面的免费课程。
mobx redux