Реагировать медленно. Виртуальная модель DOM работает быстро, но сама реагирует медленно. Он отображается каждый раз, когда вы меняете какое-либо свойство любого компонента, если вы не настроили некоторые методы для предотвращения нежелательной визуализации.

Рендеринг не обязательно означает обновление DOM - поэтому ваше простое приложение React никогда не достигнет пределов производительности. Но начиная со среднего уровня он будет отставать без дополнительных настроек. В одном из наших больших приложений для реагирования мы обнаружили, что список таблиц данных с бесконечной прокруткой довольно медленный вскоре после того, как мы начали добавлять дополнительные пользовательские функции, такие как встроенное редактирование.

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

В нашей команде мы большие поклонники библиотеки перекомпоновка, которая превращает работу с компонентами в функциональное удовольствие в удовольствие. Я часто буду ссылаться на это в этом посте. Я также помещу аннотации типов в формате flow.js. Не спрашивайте, почему это не машинопись.

Чистый компонент

Хорошо известный метод жизненного цикла React shouldComponentUpdate() по умолчанию возвращает true, что означает, что ваш компонент всегда будет отображать после получения нового набора свойств. Обычная настройка этой проблемы - React Чистый компонент или чистый () HOC перекомпоновки, которые предотвращают рендеринг, если следующие свойства поверхностно равны текущим.

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

Очевидно, что не всегда можно сохранить примитивные свойства компонентов. Поверхностное сравнение не будет эффективно работать с массивами и объектами (на самом деле может, как объяснено в следующем разделе). Вы должны поместить явное глубокое или конкретное сравнение в shouldComponentUpdate(). В приведенном ниже примере Recipients передается с массивом, который создается при повторном рендеринге каждого родителя, но в свойстве shouldUpdate() justid каждого объекта этого массива сравнивается, чтобы решить, новый это массив или нет:

Как упоминалось ранее, еще один способ - это глубокое сравнение на равенство. Есть несколько способов сделать это довольно быстро, я расскажу об этом в следующем разделе.

Не передавать новые данные в свойствах

Как я упоминал в предыдущем разделе, поверхностное сравнение массивов и объектов может действительно работать эффективно. Это потому, что это делается по ссылке.

Представьте себе чистые компоненты с несколькими встроенными свойствами массива / объекта:

Messages и ActiveUsers чистые, но все равно отображаются при каждом рендеринге родительского объекта. Даже на самом деле их свойства не изменились - массив имеет те же элементы, объект имеет те же ключи / значения, функция делает то же самое.

Это происходит потому, что встроенные значения воссоздаются при вызове родительской функцииrender() - создаются новые экземпляры массива / объекта / функции. И shouldUpdate() возвращает true при поверхностном сравнении текущего и следующего свойств.

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

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

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

Теперь взглянем на свойство [onNewMessage]. Чтобы не передавать каждый раз новую функцию, давайте создадим функцию только один раз, используя withHandlers(). Как указано в документации, результат сохраняется при рендеринге. Итак, props.notify() из приведенного ниже примера никогда не изменится в течение жизненного цикла компонента, он всегда останется одним и тем же экземпляром:

Теперь мы можем полностью переписать пример:

Redux - это водопад

Если вы используете redux в своем приложении, то очень важной целью для оптимизации является метод connect() response-redux, который создает новые свойства в своем mapStateToProps() параметре при каждом обновлении состояния, в каждом (!) компоненте, к которому он применяется. Разработчики часто упускают этот факт из виду.

Хорошая новость в том, что connect() поверхностно сравнивает эти свойства, поэтому, если вы просто берете примитивы из состояния redux или берете массивы / объекты непосредственно из состояния и переходите к своему компоненту (не создавая на их основе новых свойств), тогда все в порядке.

Если вы старались изо всех сил, но все же вам нужно создать новые непримитивные свойства на основе состояния редукции, объедините withHandlers() и withPropsOnChange(), чтобы избежать ложных обновлений. Точно так же, как вы применяете эти hoc к собственным свойствам, примените их к тем, которые происходят из состояния redux.

Используйте помощники неизменяемости

Как я упоминал в одном из предыдущих разделов, есть хороший способ провести быстрое глубокое сравнение на равенство. Это очень важно, если вы не можете избежать передачи встроенных массивов / объектов в свойства. Библиотека называется immutable.js и, как следует из названия, работает с неизменяемыми структурами данных (вам обязательно стоит прочитать об этой концепции).

Почему глубокое сравнение на равенство выполняется быстро с этой библиотекой, заключается в том, что она просто сравнивает ссылки. Каждый раз, когда вы манипулируете данными, он возвращает новый объект. Но части этого объекта, которые не были изменены во время манипуляций с исходным объектом, копируются как ссылки. Этот фрагмент кода может помочь лучше понять его https://jsbin.com/pulusubude/edit?js,console

Предыдущий пример можно переписать с помощью глубокого сравнения на равенство на основе immutable.js. Теперь нет необходимости в вычислении родительских свойств, поскольку дочерний элемент может выполнять быстрое глубокое сравнение, поэтому мы можем сохранить первую не измененную версию Chat, если мы напишем ActiveUsers как:

Я бы также рекомендовал использовать структуры данных immutable.js в редукторах. Помимо того, что он полезен при глубоком сравнении равенства, он делает управление вашим состоянием намного более удобным по сравнению со старомодным оператором распространения:

Вы также можете прочитать об этом в документации redux http://redux.js.org/docs/recipes/UsingImmutableJS.html#make-your-entire-redux-state-tree-an-immutablejs-object

В заключении

Забудьте об оптимизации в небольших приложениях, она замедлит вас и отвлечет от реальных вещей. Все сказанное выше имеет смысл, если ваше приложение наберет что-то вроде более 20 тысяч строк кода или вы работаете с большими данными, такими как действительно длинные списки или тяжелые интерактивные диаграммы. В противном случае отреагируйте, и vDOM отлично справится со своей задачей без каких-либо дополнительных действий. Лучше внедрять новые функции для своих пользователей, чем разбираться с какими-то ботаниками под капотом!

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

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

Ссылки

Эти ссылки взяты из официальных документов react и redux и рекомендованы к прочтению. Это самые основы оптимизации производительности в react и redux.

Оттофеллер - компания по разработке программного обеспечения, специализирующаяся на интерфейсной части сложных веб-приложений и перспективных передовых технологиях.