Методы улучшения и оптимизации производительности в приложениях React
Опубликовано: 2022-03-10React позволяет веб-приложениям быстро обновлять свои пользовательские интерфейсы (UI), но это не означает, что ваше среднее или крупное приложение React будет работать эффективно. Его производительность будет зависеть от того, как вы используете React при его создании, а также от вашего понимания того, как работает React, и процесса, посредством которого компоненты проходят различные этапы своего жизненного цикла. React предлагает множество улучшений производительности веб-приложений, и вы можете добиться этих улучшений с помощью различных методов, функций и инструментов.
В этом руководстве мы обсудим различные методы оптимизации производительности в приложениях React, а также функции React, которые мы можем использовать для повышения производительности.
С чего начать оптимизацию производительности в приложении React?
Мы не можем начать оптимизировать приложение, не зная точно, когда и где оптимизировать. Вы можете спросить: «С чего начать?»
Во время начального процесса рендеринга React строит DOM-дерево компонентов. Итак, когда данные изменяются в дереве DOM, мы хотим, чтобы React повторно отображал только те компоненты, на которые повлияло изменение, пропуская другие компоненты в дереве, которые не были затронуты.
Однако React может в конечном итоге перерендерить все компоненты в дереве DOM, даже если затронуты не все. Это приведет к увеличению времени загрузки, потере времени и даже трате ресурсов процессора. Мы должны предотвратить это. Итак, здесь мы сосредоточим наши усилия по оптимизации.
В этой ситуации мы могли бы настроить каждый компонент на рендеринг или сравнение только при необходимости, чтобы не тратить ресурсы и время.
Измерение производительности
Никогда не начинайте процесс оптимизации вашего приложения React, основываясь на своих ощущениях. Вместо этого используйте доступные инструменты измерения, чтобы проанализировать производительность вашего приложения React и получить подробный отчет о том, что может замедлять его работу.
Анализ компонентов React с помощью вкладки производительности Chrome
Согласно документации React, пока вы все еще находитесь в режиме разработки, вы можете использовать вкладку «Производительность» в браузере Chrome, чтобы визуализировать, как компоненты React монтируются, обновляются и отключаются. Например, на изображении ниже показано профилирование вкладки «Производительность» в Chrome и анализ моего блога в режиме разработки.

Для этого выполните следующие действия:
- Временно отключите все расширения, особенно инструменты разработчика React, потому что они могут испортить результат анализа. Вы можете легко отключить расширения, запустив браузер в режиме инкогнито.
- Убедитесь, что приложение работает в режиме разработки. То есть приложение должно быть запущено на вашем локальном хосте.
- Откройте Инструменты разработчика Chrome, перейдите на вкладку «Производительность», а затем нажмите кнопку «Запись».
- Выполните действия, которые вы хотите профилировать. Не записывайте более 20 секунд, иначе Chrome может зависнуть.
- Остановите запись.
- События React будут сгруппированы под меткой «User Timing».
Цифры из профилировщика относительные. В большинстве случаев и компоненты будут отображаться быстрее в рабочей среде. Тем не менее, это должно помочь вам выяснить, когда пользовательский интерфейс обновляется по ошибке, а также насколько глубоко и как часто происходят обновления пользовательского интерфейса.
Профилировщик инструментов разработчика React
Согласно документации React, в react react-dom
16.5+ и react react-native
0.57+ расширенные возможности профилирования доступны в режиме разработчика с использованием React Developer Tools Profiler. Профилировщик использует экспериментальный API-интерфейс React Profiler для сопоставления информации о времени каждого визуализируемого компонента, чтобы выявить узкие места производительности в приложении React.
Просто загрузите инструменты разработчика React для своего браузера, а затем вы сможете использовать инструмент профилировщика, который поставляется вместе с ним. Профилировщик можно использовать только в режиме разработки или в рабочей сборке React v16.5+. На изображении ниже представлена сводка профилировщика моего блога в режиме разработки с использованием React Developer Tools Profiler:

Для этого выполните следующие действия:
- Загрузите инструменты разработчика React.
- Убедитесь, что ваше приложение React находится либо в режиме разработки, либо в рабочей сборке React v16.5+.
- Откройте вкладку Chrome «Инструменты разработчика». Будет доступна новая вкладка «Profiler», предоставляемая React Developer Tools.
- Нажмите кнопку «Запись» и выполните действия, которые вы хотите профилировать. В идеале остановите запись после того, как вы выполнили действия, которые хотите профилировать.
- График (известный как пламенный график) появится со всеми обработчиками событий и компонентами вашего приложения React.
Примечание . Дополнительные сведения см. в документации.
Мемоизация с помощью React.memo()
React v16 был выпущен с дополнительным API, компонентом более высокого порядка под названием React.memo()
. Согласно документации, это существует только как оптимизация производительности .
Его название « memo » происходит от мемоизации, которая в основном представляет собой форму оптимизации, используемую в основном для ускорения кода путем сохранения результатов вызовов дорогостоящих функций и возврата сохраненного результата всякий раз, когда та же самая дорогостоящая функция вызывается снова.
Мемоизация — это метод однократного выполнения функции, обычно чистой функции, с последующим сохранением результата в памяти. Если мы попытаемся снова выполнить эту функцию с теми же аргументами, что и раньше , она просто вернет ранее сохраненный результат выполнения первой функции без повторного выполнения функции.
Сопоставляя приведенное выше описание с экосистемой React, упомянутые функции являются компонентами React, а аргументы — реквизитами.
Поведение по умолчанию компонента, объявленного с помощью React.memo()
, заключается в том, что он отображается только в том случае, если реквизиты в компоненте изменились. Он выполняет поверхностное сравнение реквизита, чтобы проверить это, но доступна опция, позволяющая переопределить это.
React.memo()
повышает производительность приложения React, избегая повторного рендеринга компонентов, реквизиты которых не изменились, или когда повторный рендеринг не требуется.
Приведенный ниже код является основным синтаксисом React.memo()
:
const MemoizedComponent = React.memo((props) => { // Component code goes in here })
Когда использовать React.memo()
- Чисто функциональный компонент
Вы можете использоватьReact.memo()
, если ваш компонент функционален, имеет одни и те же реквизиты и всегда выводит один и тот же результат. Вы также можете использоватьReact.memo()
на не чисто функциональных компонентах с хуками React. - Компонент часто отображается
Вы можете использоватьReact.memo()
для переноса часто отображаемого компонента. - Компонент перерисовывается с теми же реквизитами
ИспользуйтеReact.memo()
, чтобы обернуть компонент, который обычно предоставляется с теми же реквизитами во время повторного рендеринга. - Средние и высокие элементы
Используйте его для компонента, который содержит среднее или большое количество элементов пользовательского интерфейса, чтобы проверить реквизиты на равенство.
Примечание . Будьте осторожны при запоминании компонентов, которые используют реквизиты в качестве обратных вызовов. Обязательно используйте один и тот же экземпляр функции обратного вызова между рендерингами. Это связано с тем, что родительский компонент может предоставлять разные экземпляры функции обратного вызова при каждом рендеринге, что приведет к прерыванию процесса мемоизации. Чтобы исправить это, убедитесь, что мемоизированный компонент всегда получает один и тот же экземпляр обратного вызова.

Давайте посмотрим, как мы можем использовать мемоизацию в реальной ситуации. Приведенный ниже функциональный компонент под названием «Фото» использует React.memo()
для предотвращения повторного рендеринга.
export function Photo({ title, views }) { return ( <div> <div>Photo title: {title}</div> <div>Location: {location}</div> </div> ); } // memoize the component export const MemoizedPhoto = React.memo(Photo);
Приведенный выше код состоит из функционального компонента, который отображает div, содержащий заголовок фотографии и расположение объекта на фотографии. Мы также запоминаем компонент, создавая новую функцию и называя ее MemoizedPhoto
. Запоминание компонента фото предотвратит повторный рендеринг компонента, если реквизиты, title
и location
будут одинаковыми при последующих рендерингах.
// On first render, React calls MemoizedPhoto function. <MemoizedPhoto title="Effiel Tower" location="Paris" /> // On next render, React does not call MemoizedPhoto function, // preventing rendering <MemoizedPhoto title="Effiel Tower" location="Paris" />
Здесь React вызывает мемоизированную функцию только один раз. Он не будет отображать компонент при следующем вызове, пока свойства остаются прежними.
Объединение и минификация
В одностраничных приложениях React мы можем объединить и минимизировать весь наш код JavaScript в один файл. Это нормально, пока наше приложение относительно небольшое.
По мере роста нашего приложения React объединение и минимизация всего нашего кода JavaScript в один файл становится проблематичным, трудным для понимания и утомительным. Это также повлияет на производительность и время загрузки нашего приложения React, поскольку мы отправляем в браузер большой файл JavaScript. Итак, нам нужен какой-то процесс, который поможет нам разделить кодовую базу на различные файлы и доставить их в браузер с нужными интервалами.
В подобной ситуации мы можем использовать какую-либо форму сборщика ресурсов, например Webpack, а затем использовать его функции разделения кода, чтобы разделить наше приложение на несколько файлов.
Разделение кода предлагается в документации Webpack как средство сокращения времени загрузки приложения. В документации React также предлагается отложенная загрузка (обслуживание только того, что в данный момент необходимо пользователю), что может значительно повысить производительность.
Webpack предлагает три основных подхода к разделению кода:
- Точки входа
Разделите код вручную, используя конфигурацию входа. - Предотвращение дублирования
ИспользуйтеSplitChunksPlugin
для дедупликации и разделения фрагментов. - Динамический импорт
Разделите код с помощью встроенных вызовов функций внутри модулей.
Преимущества разделения кода
- Разделение кода помогает с ресурсами кэша браузера и с кодом, который не часто меняется.
- Это также помогает браузеру загружать ресурсы параллельно, что сокращает общее время загрузки приложения.
- Это позволяет нам разбивать код на куски, которые будут загружаться по запросу или по мере необходимости для приложения.
- Он сохраняет начальную загрузку ресурсов при первом рендеринге относительно небольшой, тем самым сокращая время загрузки приложения.

Неизменяемые структуры данных
В документации React говорится о возможности не изменять данные. Любые данные, которые нельзя изменить, являются неизменяемыми. Неизменяемость — это концепция, которую должны понимать программисты React.
Неизменяемое значение или объект нельзя изменить. Итак, когда происходит обновление, в памяти создается новое значение, оставляя старое нетронутым.
Мы можем использовать неизменяемые структуры данных и React.PureComponent
для автоматической проверки сложного изменения состояния. Например, если состояние в вашем приложении неизменяемо, вы можете фактически сохранить все объекты состояния в одном хранилище с помощью библиотеки управления состоянием, такой как Redux, что позволит вам легко реализовать функции отмены и повтора.
Не забывайте, что мы не можем изменить неизменяемые данные после их создания.
Преимущества неизменяемых структур данных
- У них нет побочных эффектов.
- Неизменяемые объекты данных легко создавать, тестировать и использовать.
- Они помогают нам написать логику, которую можно использовать для быстрой проверки обновлений состояния без необходимости проверять данные снова и снова.
- Они помогают предотвратить временную связь (тип связи, при которой код зависит от порядка выполнения).
Следующие библиотеки помогают предоставить набор неизменяемых структур данных:
- неизменяемость-помощник
Измените копию данных без изменения источника. - Неизменяемый.js
Неизменяемые постоянные коллекции данных для JavaScript повышают эффективность и простоту. - бесшовно-неизменяемый
Неизменяемые структуры данных для JavaScript становятся обратно совместимыми с обычными массивами и объектами JavaScript. - Реагировать-копировать-писать
Это дает неизменное состояние с изменяемым API.
Другие методы повышения производительности
Используйте производственную сборку перед развертыванием
Документация React предлагает использовать минимизированную производственную сборку при развертывании вашего приложения.

Избегайте анонимных функций
Поскольку анонимным функциям не назначается идентификатор (через const/let/var
), они не сохраняются всякий раз, когда компонент неизбежно снова отрисовывается. Это заставляет JavaScript выделять новую память каждый раз, когда этот компонент повторно отображается, вместо того, чтобы выделять один кусок памяти только один раз, например, когда используются именованные функции.
import React from 'react'; // Don't do this. class Dont extends Component { render() { return ( <button onClick={() => console.log('Do not do this')}> Don't </button> ); } } // The better way class Do extends Component { handleClick = () => { console.log('This is OK'); } render() { return ( <button onClick={this.handleClick}> Do </button> ); } }
В приведенном выше коде показаны два разных способа заставить кнопку выполнять действие при нажатии. Первый блок кода использует анонимную функцию в onClick()
, и это повлияет на производительность. Второй блок кода использует именованную функцию в функции onClick()
, что является правильным способом в этом сценарии.
Монтаж и демонтаж компонентов часто обходится дорого
Использование условных выражений или тенариев для исчезновения компонента (т. е. для его размонтирования) не рекомендуется, потому что исчезновение компонента вызовет перерисовку и перекомпоновку браузера. Это дорогостоящий процесс, поскольку позиции и геометрия HTML-элементов в документе должны быть пересчитаны. Вместо этого мы можем использовать свойства opacity
и visibility
CSS, чтобы скрыть компонент. Таким образом, компонент по-прежнему будет находиться в DOM, но будет невидимым без потери производительности.
Виртуализируйте длинные списки
Документация предполагает, что если вы визуализируете список с большим объемом данных, вы должны отображать небольшую часть данных в списке за раз в пределах видимого окна просмотра. Затем вы можете отображать больше данных по мере прокрутки списка; следовательно, данные отображаются только тогда, когда они находятся в области просмотра. Этот процесс называется «окно». При работе с окнами в любой момент времени визуализируется небольшое подмножество строк. Для этого существуют популярные библиотеки, две из которых поддерживаются Брайаном Воном:
- окно реакции
- реактивно-виртуализированный
Заключение
Существует несколько других способов повышения производительности вашего приложения React. В этой статье были рассмотрены наиболее важные и эффективные методы оптимизации производительности.
Надеюсь, вам понравилось читать этот урок. Вы можете узнать больше с помощью ресурсов, перечисленных ниже. Если у вас есть какие-либо вопросы, оставьте их в разделе комментариев ниже. С удовольствием отвечу на каждый из них.
Ссылки и связанные ресурсы
- «Оптимизация производительности», React Docs
- «Используйте React.memo с умом», Дмитрий Павлютин
- «Методы оптимизации производительности в React», Нитеш Ядав
- «Неизменяемость в React: нет ничего плохого в мутирующих объектах», Эстебан Эррера
- «10 способов оптимизировать производительность вашего приложения React», Чидуме Ннамди
- «5 советов по повышению производительности ваших приложений React», Уильям Ле