Подтвердить что ты не робот

Может ли приложение React-Redux действительно масштабироваться, а также, скажем, Backbone? Даже с повторным выбором. На мобильных устройствах

В Redux каждое изменение в хранилище запускает notify для всех подключенных компонентов. Это делает вещи очень простыми для разработчика, но что, если у вас есть приложение с N связанными компонентами, а N очень велико?

Каждое изменение в хранилище, даже если оно не связано с компонентом, все еще запускает shouldComponentUpdate с простым тестом === на пути reselect ed хранилища. Это быстро, правда? Конечно, может быть, один раз. Но N раз, для каждого изменения? Это фундаментальное изменение в дизайне заставляет меня подвергнуть сомнению истинную масштабируемость Redux.

В качестве дополнительной оптимизации можно выполнить все вызовы notify, используя _.debounce. Тем не менее, тестирование N === для каждого изменения магазина и обработки другой логики, например логики представления, похоже на средство для завершения.

Я работаю над мобильным и мобильным приложениями для мобильных и мобильных приложений с миллионами пользователей и перехожу от Backbone to Redux. В этом приложении пользователю предоставляется swipeable интерфейс, который позволяет им перемещаться между различными стеками представлений, подобно Snapchat, за исключением того, что каждый стек имеет бесконечную глубину. В самом популярном типе просмотра бесконечный скроллер эффективно обрабатывает загрузку, рендеринг, прикрепление и отключение элементов фида, например, сообщение. Для задействованного пользователя нередко прокручивается сотни или тысячи сообщений, затем вводится пользовательский фид, другой пользовательский канал и т.д. Даже при большой оптимизации количество подключенных компонентов может быть очень большим.

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

Я что-то упустил, или Redux принципиально испорчен для большого приложения?

4b9b3361

Ответ 1

Это не проблема, присущая Redux IMHO.

Кстати, вместо того, чтобы пытаться отображать 100k компонентов одновременно, вы должны попытаться подделать его с помощью lib, например react-infinite или что-то подобное, и отображать только видимые (или близкие к нему) элементы вашего списка. Даже если вам удастся отобразить и обновить список 100 тыс., Он все еще не работает и требует большой памяти. Вот несколько Рекомендации LinkedIn

Этот anwser рассмотрит, что вы по-прежнему пытаетесь отображать обновляемые элементы 100k в своей DOM и что вы не хотите, чтобы слушатели 100k (store.subscribe()) вызывались при каждом изменении.


2 школы

При разработке приложения пользовательского интерфейса функциональным способом вы в основном имеете 2 варианта:

Всегда выводить с самого верхнего уровня

Он работает хорошо, но включает в себя больше шаблонов. Это не совсем предложенный метод Redux, но достижимый, с некоторыми недостатками. Обратите внимание, что даже если вам удастся подключиться к одному редукционному соединению, вам все равно придется называть много shouldComponentUpdate во многих местах. Если у вас есть бесконечный стек представлений (например, рекурсия), вам придется отображать в качестве виртуального dom все промежуточные представления, а shouldComponentUpdate будет вызываться на многих из них. Таким образом, это не очень эффективно, даже если у вас есть одно подключение.

Если вы не планируете использовать методы жизненного цикла React, а используете только чистые функции рендеринга, тогда вам, вероятно, следует рассмотреть другие аналогичные параметры, которые будут сосредоточены только на этой задаче, например deku (который можно использовать с Redux)

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

Подключение данных к дочерним компонентам

Это то, что предлагает react-redux, используя connect. Поэтому, когда государство изменяется, и оно связано только с более глубоким ребенком, вы только визуализируете этого ребенка и не должны отображать компоненты верхнего уровня каждый раз, как поставщики контекста (сокращение/intl/custom...), а также основной макет приложения. Вы также не вызываете shouldComponentUpdate для других дочерних элементов, потому что он уже запекается в слушателе. Вызов множества очень быстрых слушателей, вероятно, быстрее, чем рендеринг, каждый раз, когда промежуточные компоненты реагируют, а также позволяет уменьшить количество шаблонов, проходящих через реквизит, поэтому для меня это имеет смысл при использовании с React.

Также обратите внимание, что сравнение идентичности происходит очень быстро, и вы можете легко их выполнять при каждом изменении. Помните Angular грязную проверку: некоторым людям удалось создать реальные приложения с этим! И сравнение идентичности происходит намного быстрее.


Понимание вашей проблемы

Я не уверен, что понимаю всю вашу проблему, но я понимаю, что у вас есть представления с похожими элементами в 100 000, и вам интересно, следует ли использовать connect со всеми этими 100k элементами, потому что вызывать 100k слушателей при каждом изменении кажется дорогостоящим.

Эта проблема кажется присущей природе выполнения функционального программирования с пользовательским интерфейсом: список был обновлен, поэтому вам нужно повторно отобразить список, но, к сожалению, это очень длинный список и кажется неэффективным... С Backbone вы могли бы взломать что-то, чтобы только отнести ребенка. Даже если вы выведете этого ребенка с помощью React, вы должны вызвать рендеринг в императивном порядке, вместо того, чтобы просто объявлять "когда список изменяется, повторно отобразить его".


Решение вашей проблемы

Очевидно, что подключение элементов списка 100k кажется удобным, но не является показательным из-за вызова прослушивателей 100 k реагирования-редукта, даже если они быстры.

Теперь, если вы подключаете большой список из 100 тыс. элементов вместо отдельных элементов отдельно, вы вызываете только один прослушиватель rec-redux, а затем должны эффективно отображать этот список.


Наивное решение

Итерирование над 100 тыс. элементов для их рендеринга, что приводит к 99999 элементам, возвращающим false в shouldComponentUpdate и одному повторному рендерингу:

list.map(item => this.renderItem(item))

Решение для исполнителей 1: пользовательский connect + усилитель хранения

Метод React-Redux connect - это просто Компонент более высокого порядка (HOC), который вводит данные в обернутый компонент, Для этого он регистрирует прослушиватель store.subscribe(...) для каждого подключенного компонента.

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

  • Усовершенствователь магазина

Выставить дополнительный метод store.subscribeItem(itemId,listener)

Оберните dispatch, чтобы всякий раз, когда действие, связанное с элементом, отправляется, вы вызываете зарегистрированного слушателя (ов) этого элемента.

Хорошим источником вдохновения для этой реализации может быть redux-batched-subscribe.

  1. Пользовательское подключение

Создайте компонент более высокого порядка с помощью API, например:

Item = connectItem(Item)

HOC может ожидать свойство itemId. Он может использовать расширенное хранилище Redux из контекста React, а затем зарегистрировать его прослушиватель: store.subscribeItem(itemId,callback). Исходный код оригинала connect может служить базовым вдохновением.

  1. HOC приведет только к повторному рендерингу, если элемент изменится

Связанный ответ: fooobar.com/questions/113061/...

Связанная проблема с редукцией: https://github.com/rackt/react-redux/issues/269


Выполняющее решение 2: попытки вектора

Более эффективный подход предполагает использование постоянной структуры данных, например vector trie:

Trie

Если вы представляете список ваших 100 тыс. позиций как три, каждый промежуточный node имеет возможность коротко обрезать рендеринг, что позволяет избежать большого количества shouldComponentUpdate в дочерних элементах.

Этот метод можно использовать с ImmutableJS, и вы можете найти некоторые эксперименты, которые я сделал с ImmutableJS: Реальная производительность: рендеринг большого списка с помощью PureRenderMixin Однако у этого есть недостатки, поскольку libs, такие как ImmutableJs, еще не предоставляют публичные/стабильные API для этого (issue), и мое решение загрязняет DOM с некоторыми бесполезными промежуточными узлами <span> (issue).

Вот JsFiddle, который демонстрирует, как эффективный рендеринг списка объектов из 100 тысяч объектов ImmutableJS. Первоначальный рендеринг довольно длинный (но я думаю, вы не инициализируете свое приложение со 100 тыс. Элементов!), Но после того, как вы заметите, что каждое обновление приводит к небольшому количеству shouldComponentUpdate. В моем примере я только обновляю первый элемент каждую секунду, и вы замечаете, даже если в списке есть 100 тыс. Элементов, для этого требуется только 110 вызовов shouldComponentUpdate, что гораздо более приемлемо!:)

Изменить: кажется, что ImmutableJS не так хорош, чтобы сохранить свою неизменную структуру для некоторых операций, таких как вставка/удаление элементов в случайном индексе. Вот JsFiddle, который демонстрирует производительность, которую вы можете ожидать в соответствии с операцией в списке. Удивительно, но если вы хотите добавить много элементов в конец большого списка, вызов list.push(value) много раз, кажется, сохранит гораздо больше структуру дерева, чем вызов list.concat(values).

Кстати, документировано, что List эффективен при изменении ребер. Я не думаю, что эти плохие показатели при добавлении/удалении по заданному индексу связаны с моей техникой, а скорее связаны с основной реализацией списка ImmutableJs.

Списки реализуют Deque с эффективным добавлением и удалением из конца (push, pop) и begin (unshift, shift).

Ответ 2

Это может быть более общий ответ, чем вы ищете, но в целом:

  • Рекомендация из документов Redux заключается в том, чтобы связать компоненты React с довольно высоким уровнем иерархии компонентов. См. этот раздел.. Это позволяет управлять количеством подключений, и вы можете просто передать обновленные реквизиты в дочерние компоненты.
  • Частью мощности и масштабируемости React является предотвращение рендеринга невидимых компонентов. Например, вместо того, чтобы устанавливать класс invisible в DOM-элементе, в React мы просто не отображаем компонент вообще. Повторная передача компонентов, которые не изменились, также не является проблемой, так как виртуальный DOM-процесс оптимизирует низкоуровневые DOM-взаимодействия.