Перейти к содержанию

Производительность

Насколько хорошо “масштабируется” Redux с точки зрения производительности и архитектуры?

Пока еще нет окончательного ответа на этот вопрос, в большинстве случаев это не должно быть проблемой.

Работа Redux как правило, делится на несколько частей:

  • обработка действие в мидлварах и редьюсерах (включая дублирование объекта для постоянных обновлений),
  • уведомление подписчиков после отправки действий,
  • обновление UI-компонентов на основе изменения состояния.

Хотя, конечно, каждый из этих пунктов может ухудшить производительность в достаточно сложных ситуациях, но в реализации нет Redux изначально нет ничего медлительного или неэффективного. По факту, React Redux сильно оптимизирован в части избавлениях от ненужных перерисовок, а React-Redux v5 показывает заметные улучшение по сравнению с более поздними версиями.

Redux может быть не так эффективен из коробки, по сравнению с другими библиотеками. Для максимальной производительности отрисовки в React-приложении, состояние должно храниться в нормализованной форме, множество компонентов должны подключаться к одному стору, а не к нескольким, и подключенный список компонентов должен передавать идентификатор каждого элемента своим подключенным потомкам (позволяя тем самым элементам списка искать их собственные данные по ID). Это минимализирует общее количество перерисовок. Использование мемоизированных функций также является важным фактором эффективности.

С точки зрения архитектуры, неофициальные данные показывают, что Redux хорошо работает с различными проектами и размерами команды. В настоящее время Redux используется сотнями компаний и тысячами разработчиков, имеет несколько сотен тысяч ежемесячных установок из NPM. Один разработчик сообщил:

для сравнения, у нас есть ~500 типов экшенов, ~400 редьюсеров, ~150 компонентов, 5 мидлваров, ~200 экшенов, ~2300 тестов

Статьи

Обсуждения

Не будет ли вызов “всех моих редьюсеров” для каждого действия медленным?

Важно иметь ввиду, что Redux-стор на самом деле имеет всего одну функцию-редьюсер. Стор передает текующее состояние и отправленный экшен этому единственному редьюсеру и позволяет ему обрабатывать это все как надо.

Очевидно, попытка обрабатывать каждое возможное действие в одной функции плохо масштабируется, хотя бы с точки зрения размера функции и читабельности. Таким образом, есть смысл разделить реальную работу на отдельные функции, которые могут быть вызваны редьюсером верхнего порядка. В частности, общий рекомендуемый подход — это иметь отдельную функцию (подредьюсер), которая ответственна за управление обновлениями на определенном участке состояния в определенном ключе. Функция combineReducers(), поставляемая с Redux, — 1 из множества способов реализации этого подхода. Также настоятельно рекомендуется держать состояние стора единообразным и нормализированным на столько, на сколько это возможно. В конечном счете, только Вы отвечаете за организацию логики Вашего редьюсера любыми способами, какими только пожелаете.

Однако, даже если у Вас много различных редьюсеров, объединенных вместе, даже с глубоко вложенным состоянием, скорость редьюсера вряд ли будет проблемой. Движки JavaScript способны запускать очень большое количество вызовов функций в секунду, а большинство Ваших редьюсеров вероятнее всего используют конструкцию switch и возвращают существующее состояние по умолчанию в ответ на большинство действий.

Если вы действительно обеспокоены производительностью редьюсера, вы можете использовать такие утилиты, как redux-ignore или reduxr-scoped-reducer, чтоб гарантировать, что только определенные редьюсеры прослушивают конкретные действия. Также Вы можете использовать redux-log-slow-reducers, чтобы провести сравнительный анализ эффективности.

Обсуждения

Должен ли я иметь полноценный клон моего состояния в редьюсере? Не будет ли копирование моего состояния медленным?

Иммутабельное обновление состояния в большинстве случаев подразумевает создание поверхностных, неглубоких копий. Поверхностные копии более быстрые, чем полноценные, потому что предполагают копирование небольших объектов и полей, и это эффективно снижает перемещение нескольких указателей.

Однако, Вам надо создавать скопированные и обновленные объекты для каждого затронутого уровня вложенности. Хотя это не должно быть особенно затратно, но это еще одна причина, почему Вам следует держать Ваше состояние нормализованным и поверхностным на сколько возможно.

Распростораненное заблуждение Redux: Вам надо полноценно клонировать состояние. На самом деле: если внутри ничего не меняется, достаточно хранить ссылку!

Обсуждения

Как мне уменьшить количество событий обновления стора?

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

Если Вы используете React, помните, что Вы можете улучшить производительность нескольких синхронных отправок обертыванием их в ReactDOM.unstable_batchedUpdates(), но этот API экспериментален и может быть удален в любом выпуске React, поэтому не полагайтесь на это слишком сильно. Взгляните на аналоги:

  • redux-batched-actions — редьюсер высокого порядка, который позволяет Вам отправлять несколько действий так, будто это одно действие, и “распаковывать” их в редьюсере,
  • redux-batched-subscribe — расширитель стора, который позволяет Вам вызывать подписчиков для множественных отправок не чаще, чем раз в определенное время (debounce),
  • redux-batch — расширитель стора, которых обрабатывает отправку набора экшенов с уведомлением одного подписчика.

Обсуждения

Библиотеки

Будут ли проблемы с памятью из-за использования “одного дерева состояния”? Будет ли вызов большого количества действий занимать память?

Во первых, с точки зрения использования ресурсов памяти, Redux не сильно отличается от других JavaScript-библиотек. Единственно отличие — все возможные ссылки на объекты вместе вложены в одно дерево, а не хранятся в различных независимах экземплярах модели, как например в Backbone.

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

Наконец, Redux держит в памяти в один момент времени только одно дерево состояния со ссылками. Объекты, которые больше не имеют ссылок в этом дереве, обычно будут собраны сборщиком мусора.

Redux сам не хранит историю действий. Однако, Redux DevTools — хранит, таким образом действия могут быть проиграны заново, но это возможно только в процессе разработки и не используется в продакшн-версии.

Документация

Обсуждения