Статьи

Redux против MobX: что лучше для вашего проекта?

Для многих разработчиков JavaScript самая большая претензия к Redux — количество стандартного кода, необходимого для реализации функций. Лучшей альтернативой является MobX, который предоставляет аналогичную функциональность, но с меньшим количеством кода для написания.

Для новичков MobX взгляните на это введение, написанное создателем MobX. Вы также можете пройти этот урок, чтобы получить практический опыт.

Цель этой статьи — помочь разработчикам JavaScript решить, какое из этих двух решений для управления состоянием лучше всего подходит для их проектов. Я перенес этот проект CRUD Redux в MobX, чтобы использовать его в качестве примера в этой статье. Сначала я расскажу о плюсах и минусах использования MobX, а затем продемонстрирую примеры кода из обеих версий, чтобы показать разницу.

Код для проектов, упомянутых в этой статье, можно найти на GitHub:

Если вам понравился этот пост, вы также можете зарегистрироваться в SitePoint Premium и посмотреть наш курс по работе с формами с использованием React и Redux .

Кусочки данных в форме птиц, мигрирующие Redux в MobX

Что общего у Redux и MobX?

Во-первых, давайте посмотрим, что общего у них обоих. Oни:

  • библиотеки с открытым исходным кодом
  • обеспечить управление состоянием на стороне клиента
  • поддержка отладки путешествий во времени через расширение redux-devtools-extension
  • не привязаны к конкретной структуре
  • иметь обширную поддержку для React / React Native.

4 причины использовать MobX

Давайте теперь посмотрим на основные различия между Redux и MobX.

1. Легко учиться и использовать

Для новичка вы можете научиться пользоваться MobX всего за 30 минут. Как только вы изучите основы, вот и все. Вам не нужно учить что-то новое. С Redux основы тоже просты. Однако, как только вы начнете создавать более сложные приложения, вам придется иметь дело с:

  • обработка асинхронных действий с помощью redux-thunk
  • упрощение вашего кода с помощью Redux-Saga
  • определение селекторов для обработки вычисленных значений и т. д.

С MobX все эти ситуации «волшебным образом» решаются. Вам не нужны дополнительные библиотеки для обработки таких ситуаций.

2. Меньше кода для записи

Чтобы реализовать функцию в Redux, вам нужно обновить как минимум четыре артефакта. Это включает в себя написание кода для редукторов, действий, контейнеров и компонентов. Это особенно раздражает, если вы работаете над небольшим проектом. MobX требует, чтобы вы обновили как минимум два артефакта (то есть хранилище и компонент представления).

3. Полная поддержка объектно-ориентированного программирования

Если вы предпочитаете писать объектно-ориентированный код, вам будет приятно узнать, что вы можете использовать ООП для реализации логики управления состоянием с MobX. Используя декораторы, такие как @observable и @observer , вы можете легко сделать ваши простые компоненты JavaScript и хранилища реактивными. Если вы предпочитаете функциональное программирование, нет проблем — это также поддерживается. Redux, с другой стороны, в значительной степени ориентирован на принципы функционального программирования. Тем не менее, вы можете использовать библиотеку redux-connect-decorator, если вам нужен подход на основе классов.

4. Работать с вложенными данными легко

В большинстве приложений JavaScript вы обнаружите, что работаете с реляционными или вложенными данными. Чтобы иметь возможность использовать его в магазине Redux, вам сначала нужно его нормализовать . Затем вам нужно написать еще немного кода для управления отслеживанием ссылок в нормализованных данных.

В MobX рекомендуется хранить ваши данные в денормализованной форме. MobX может отслеживать отношения для вас, и автоматически повторно отображает изменения. Используя объекты домена для хранения ваших данных, вы можете напрямую обращаться к другим объектам домена, определенным в других хранилищах. Кроме того, вы можете использовать (@) вычисляемые декораторы и модификаторы для наблюдаемых, чтобы легко решать сложные задачи с данными.

3 причины не использовать MobX

1. Слишком много свободы

Redux — это фреймворк, обеспечивающий строгие правила написания кода состояния. Это означает, что вы можете легко писать тесты и разрабатывать поддерживаемый код. MobX является библиотекой и не имеет правил о том, как ее реализовать. Опасность заключается в том, что очень легко создавать ярлыки и применять быстрые исправления, которые могут привести к невозможности исправления кода.

2. Трудно отлаживать

Внутренний код MobX «магически» обрабатывает много логики, чтобы сделать ваше приложение реактивным. Есть невидимая область, где ваши данные проходят между хранилищем и вашим компонентом, что затрудняет отладку в случае возникновения проблем. Если вы изменяете состояние непосредственно в компонентах, без использования @actions , вам будет трудно определить источник ошибки.

3. Может быть лучшая альтернатива MobX

В разработке программного обеспечения постоянно появляются новые тенденции. В течение нескольких коротких лет современные программные технологии могут быстро потерять импульс. На данный момент существует несколько решений, конкурирующих как с Redux, так и с Mobx. Вот несколько примеров: Relay / Apollo & GraphQL , Alt.js и Jumpsuit . Любая из этих технологий может стать самой популярной. Если вы действительно хотите знать, какой из них лучше для вас, вам придется попробовать их все.

Сравнение кода: Redux против MobX

Достаточно теории, давайте посмотрим на код. Сначала мы сравним, как каждая версия выполняет загрузку.

Бутстрапирование

Версия Redux:
В Redux мы сначала определяем наш магазин, а затем передаем его в App через Provider . Нам также понадобится определить redux-promise-middleware и 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') ); 

Версия MobX:
В MobX нам нужно настроить несколько магазинов. В этом случае я использую только один магазин, который я поместил в коллекцию с именем allStores . Затем Provider используется для передачи коллекции магазинов в App .

Как упоминалось ранее, MobX не нуждается во внешних библиотеках для обработки асинхронных действий, следовательно, меньше строк. Однако нам нужно, чтобы mobx-remotedev подключился к redux-devtools-extension отладки 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 состояние и действия передаются реквизитам с помощью функции connect-) реагировать с избыточностью.

 // 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 логика для действия и редуктора сделана в одном классе. Я определил асинхронное действие, которое вызывает другие entities fetched response , полученные после получения response .

Поскольку MobX использует стиль ООП, определенный здесь класс 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); } } … 

Хотите верьте, хотите нет, но логика, определенная в обеих версиях, выполняет одинаковые задачи:

  • обновить состояние загрузки интерфейса
  • извлекать данные асинхронно
  • ловить исключения и обновлять состояние.

В Redux мы использовали 33 строки кода . В MobX мы использовали около 14 строк кода для достижения того же результата! Основным преимуществом версии MobX является то, что вы можете повторно использовать базовый код практически во всех классах хранилища доменов практически без изменений. Это означает, что вы можете построить свое приложение быстрее.

Другие отличия

Чтобы создавать формы в Redux, я использовал redux-form . В MobX я использовал форму mobx-реагировать . Обе библиотеки являются зрелыми и помогают легко обрабатывать логику форм. Лично я предпочитаю mobx-react-form , так как она позволяет вам проверять поля с помощью плагинов. С помощью redux-form вы либо пишете свой собственный код проверки, либо можете импортировать пакет проверки, чтобы обрабатывать проверку для вас.

Недостатком MobX является то, что вы не можете напрямую обращаться к определенным функциям в наблюдаемых объектах, поскольку они не являются простыми объектами JavaScript. К счастью, они предоставили функцию toJS() которую можно использовать для преобразования наблюдаемых объектов в простые объекты JavaScript.

Вывод

Ясно, что вы можете видеть, что кодовая база MobX намного меньше. Используя стиль ООП и хорошие практики разработки, вы можете быстро создавать приложения. Основным недостатком является то, что очень легко писать плохой, не поддерживаемый код.

Redux, с другой стороны, более популярен и хорошо подходит для создания больших и сложных проектов . Это строгая структура с мерами безопасности, гарантирующими, что каждый разработчик пишет код, который легко тестировать и поддерживать. Тем не менее, он не очень подходит для небольших проектов.

Несмотря на недостатки MobX, вы все равно можете создавать большие проекты, если вы следуете передовой практике. По словам Альберта Эйнштейна: «Сделай все как можно проще, но не проще».

Я надеюсь, что предоставил достаточно информации, чтобы прояснить, стоит ли переходить на MobX или придерживаться Redux. В конечном счете, решение зависит от типа проекта, над которым вы работаете, и доступных вам ресурсов.

Эта статья была рецензирована Домиником Майерсом и Вилданом Софтиком . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!


Если вы хотите улучшить свою игру Redux, зарегистрируйтесь в SitePoint Premium и зарегистрируйтесь на нашем курсе Redux Design Issues and Testing . В этом курсе вы создадите приложение Redux, которое получает твиты, организованные по темам, через соединение с веб-сокетом. Чтобы получить представление о том, что в магазине, ознакомьтесь с бесплатным уроком ниже.