Почти 60% посетителей сайта покидают его в том случае, если его загрузка занимает более 3 секунд. 80% таких посетителей на сайт уже не возвращается. Это говорит о том, что успех веб-проекта не в последнюю очередь зависит от его скорости. Автор материала, перевод которого мы сегодня публикуем, хочет рассказать о методиках повышения производительности React-приложений.
Результаты оптимизации приложения
Прежде чем я расскажу вам о том, как я ускорял приложение, хочу показать некоторые цифры. Тут измерения проведены в расчёте на то, что с приложением будут работать, используя довольно медленное по современным меркам соединение. Но надо отметить, что в реальности у большинства пользователей будут более быстрые соединения.
Как видите, разница между оптимизированным и неоптимизированным приложением достаточно велика. Особенно это заметно в медленных сетях.
Размер бандла приложения был исследован с помощью source-map-explorer. Этот инструмент также позволяет узнать о том, сколько места занимают различные библиотеки. Показатели, которые можно видеть в верхней части рисунка, измерены с помощью Google Lighthouse.
Теперь расскажу о том, как я оптимизировал приложение.
В старой версии приложения я использовал библиотеку styled-components. Чем это плохо? Дело в том, что обычный CSS быстрее и занимает меньше места. Современные браузеры умеют загружать CSS-код параллельно с JavaScript-бандлом. Кроме того, для использования обычного CSS не нужно дополнительной библиотеки. Минифицированный вариант styled-components занимает порядка 54 Кб. Использование обычного CSS вместо styled-components привело к тому, что код приложения быстрее загружается, и к тому, что при изменении стилей системе приходится выполнять меньше вычислений.
Просто отказавшись от библиотеки styled-components и перейдя на обычный CSS можно сократить время загрузки сайта примерно на 0.3 секунды
Если CSS позволяет обеспечить лучшую производительность, чем технологии, реализующие схему CSS-in-JS, можно задаться вопросом о том, почему разработчики пользуются этой технологией стилизации компонентов. Среди причин выбора CSS-in-JS можно назвать то, что эта технология позволяет ограничивать область видимости стилей и отказаться от глобальной стилизации. Её удобно применять для работы с темами приложений. А кому-то, может быть, просто нравится стилизовать React-приложения именно так.
Create-react-app теперь официально поддерживает CSS-модули с ограниченной областью видимости. Это означает, что ограничивать область применения стилей можно и без применения дополнительных библиотек.
Если вы работаете с библиотекой styled-components, то для того чтобы пользоваться переменными, определяющими темы, достаточно обернуть подобные переменные в
Если вы полагаете, что 91% — это недостаточно хороший показатель — поразмышляйте о том, что это, возможно, не так уж и мало.
Поддержка CSS-переменных
Собственно говоря, если вас не интересует поддержка IE — тогда вы смело можете использовать в своих проектах CSS-переменные. Если вам интересна эта тема — рекомендую взглянуть на данный материал.
Анализ пакета material-ui
Я — большой любитель Material Design. Для React написана замечательная Material-библиотека, которая называется material-ui. У этой библиотеки есть лишь одна проблема. Это — её размер. Она очень велика. Даже если пользоваться лишь отдельными её компонентами, в бандл попадёт её реализация механизма CSS-in-JS, а это — примерно 30 Кб минифицированного кода.
Каковы альтернативы? Я решил построить собственные компоненты, стилизуя их в процессе создания приложения. Одной из причин такого выбора было то, что мне захотелось освежить знания в области CSS. А CSS-код я не писал уже давно. Однако здесь есть и другие возможности. В частности, речь идёт о CSS-фреймворках, размеры которых гораздо меньше, чем размер material-ui. Например — это Spectre и Bulma, код которых занимает, соответственно, 9 и 40 Кб после GZIP-сжатия.
Spectre — 9 Кб после GZIP-сжатия
Bulma — 40 Кб после GZIP-сжатия
Итак, у вас есть роутер со множеством импортированных страниц. Если речь идёт о паре страниц — никаких проблем тут нет. Но по мере увеличения количества страниц растёт и время первого вывода сайта на экран. Вот как могут выглядеть команды импорта:
Как это улучшить? К нашему счастью, React может организовать ленивую загрузку страниц. Это же касается и кода компонентов, который может быть разбит на небольшие фрагменты, загружаемые при возникновении необходимости в них. Вот как это выглядит:
В прогрессивных веб-приложениях применяются сервис-воркеры. Они позволяют пользователям добавлять приложения на домашние экраны их устройств. Но этим возможности сервис-воркеров не ограничиваются. В частности, они в состоянии значительно улучшить кэширование. Это приводит к тому, что приложение, после первой загрузки, будет загружаться гораздо быстрее.
В моём исходном проекте при монтировании компонентов использовалось много анимации, которая оживляла загрузку страницы. Всё это не просто замедляло страницу. Это делало её гораздо медленнее, чем она могла бы быть. Я, глядя на сайт, и радуясь тому, как симпатично он выглядит, до определённого момента не задумывался о производительности. Но на сайте была не только анимация. Там были и другие подобные украшательства. Например — кнопка, которая позволяла перейти в верхнюю часть страницы. Например — анимированная загрузка некоторых элементов. Всё это мне нравилось, но при ближайшем рассмотрении оказалось, что, например, сайт реально тормозит на не самых быстрых устройствах. Причём, сначала я тестировал сайт исключительно на ноутбуке, поэтому узнал об этом далеко не сразу.
Ещё у меня был компонент-слайдер, который я добавил на страницу без особых размышлений о его «весе». Его я использовал для показа слайд-шоу. Как оказалось позднее, один только код этого слайдера занимал, в минифицированном виде, 30 Кб. Тогда я решил самостоятельно создать компонент для показа слайд-шоу. Его минифицированный код, в итоге, занял 25 КБ. В этот объём входила хорошая библиотека анимации и система работы с жестами, которые можно использовать не только для слайд-шоу, но и в других частях приложения. И выглядит то, что у меня получилось, гораздо лучше, чем стороннее решение.
Вот слайдер из NPM.
Слайдер из NPM
Вот мой слайдер
Слайдер собственной разработки (в жизни он работает гораздо более плавно, чем на этом GIF-изображении с низкой частотой кадров)
Посмотреть этот слайдер в действии можно здесь.
Если вы пользуетесь create-react-app, то вам очень просто проанализировать состав бандла. Для этого выполните команду
Сведения о бандле
Как видите, ускорять React-приложения не так уж и сложно. Достаточно внимательно следить за тем, из чего они построены, тестировать их и вносить в них соответствующие изменения. Вот проект, о котором шла здесь речь, до улучшения, а вот — после.
Если вы интересуетесь оптимизацией React-приложений — вот несколько наших публикаций на эту тему:
Уважаемые читатели! Есть ли у вас примеры удачной (или неудачной) оптимизации React-приложений?
Результаты оптимизации приложения
Бенчмарки
Прежде чем я расскажу вам о том, как я ускорял приложение, хочу показать некоторые цифры. Тут измерения проведены в расчёте на то, что с приложением будут работать, используя довольно медленное по современным меркам соединение. Но надо отметить, что в реальности у большинства пользователей будут более быстрые соединения.
- В ходе проведения измерений, выполненных средствами вкладки Network инструментов разработчика Chrome, скорость передачи данных была принудительно ограничена до уровня быстрого 3G-соединения.
- Показатель First Load получен при отключённом кэше.
- Показатель 2nd Load указывает на время повторной загрузки приложения при включённом кэше.
Как видите, разница между оптимизированным и неоптимизированным приложением достаточно велика. Особенно это заметно в медленных сетях.
Размер бандла приложения был исследован с помощью source-map-explorer. Этот инструмент также позволяет узнать о том, сколько места занимают различные библиотеки. Показатели, которые можно видеть в верхней части рисунка, измерены с помощью Google Lighthouse.
Теперь расскажу о том, как я оптимизировал приложение.
1. Использование CSS вместо CSS-in-JS
В старой версии приложения я использовал библиотеку styled-components. Чем это плохо? Дело в том, что обычный CSS быстрее и занимает меньше места. Современные браузеры умеют загружать CSS-код параллельно с JavaScript-бандлом. Кроме того, для использования обычного CSS не нужно дополнительной библиотеки. Минифицированный вариант styled-components занимает порядка 54 Кб. Использование обычного CSS вместо styled-components привело к тому, что код приложения быстрее загружается, и к тому, что при изменении стилей системе приходится выполнять меньше вычислений.
Просто отказавшись от библиотеки styled-components и перейдя на обычный CSS можно сократить время загрузки сайта примерно на 0.3 секунды
Если CSS позволяет обеспечить лучшую производительность, чем технологии, реализующие схему CSS-in-JS, можно задаться вопросом о том, почему разработчики пользуются этой технологией стилизации компонентов. Среди причин выбора CSS-in-JS можно назвать то, что эта технология позволяет ограничивать область видимости стилей и отказаться от глобальной стилизации. Её удобно применять для работы с темами приложений. А кому-то, может быть, просто нравится стилизовать React-приложения именно так.
▍Стили с ограниченной областью видимости
Create-react-app теперь официально поддерживает CSS-модули с ограниченной областью видимости. Это означает, что ограничивать область применения стилей можно и без применения дополнительных библиотек.
▍Темы
Если вы работаете с библиотекой styled-components, то для того чтобы пользоваться переменными, определяющими темы, достаточно обернуть подобные переменные в
ThemeProvider
. Всё это хорошо, но по состоянию на май 2019 года 91% браузеров поддерживает похожую стандартную возможность CSS.Если вы полагаете, что 91% — это недостаточно хороший показатель — поразмышляйте о том, что это, возможно, не так уж и мало.
Поддержка CSS-переменных
Собственно говоря, если вас не интересует поддержка IE — тогда вы смело можете использовать в своих проектах CSS-переменные. Если вам интересна эта тема — рекомендую взглянуть на данный материал.
2. Уход от больших CSS-библиотек
Анализ пакета material-ui
Я — большой любитель Material Design. Для React написана замечательная Material-библиотека, которая называется material-ui. У этой библиотеки есть лишь одна проблема. Это — её размер. Она очень велика. Даже если пользоваться лишь отдельными её компонентами, в бандл попадёт её реализация механизма CSS-in-JS, а это — примерно 30 Кб минифицированного кода.
Каковы альтернативы? Я решил построить собственные компоненты, стилизуя их в процессе создания приложения. Одной из причин такого выбора было то, что мне захотелось освежить знания в области CSS. А CSS-код я не писал уже давно. Однако здесь есть и другие возможности. В частности, речь идёт о CSS-фреймворках, размеры которых гораздо меньше, чем размер material-ui. Например — это Spectre и Bulma, код которых занимает, соответственно, 9 и 40 Кб после GZIP-сжатия.
Spectre — 9 Кб после GZIP-сжатия
Bulma — 40 Кб после GZIP-сжатия
3. Ленивая загрузка страниц
Итак, у вас есть роутер со множеством импортированных страниц. Если речь идёт о паре страниц — никаких проблем тут нет. Но по мере увеличения количества страниц растёт и время первого вывода сайта на экран. Вот как могут выглядеть команды импорта:
import NotFound from "pages/NotFound";
import Projects from "pages/Projects";
import Project from "pages/Project";
Как это улучшить? К нашему счастью, React может организовать ленивую загрузку страниц. Это же касается и кода компонентов, который может быть разбит на небольшие фрагменты, загружаемые при возникновении необходимости в них. Вот как это выглядит:
import React, { lazy, Suspense } from "react";
const load = (Component: any) => (props: any) => (
<Suspense fallback={<Loader />}>
<Component {...props} />
</Suspense>
);
const NotFound = load(lazy(() => import("pages/NotFound")));
const Projects = load(lazy(() => import("pages/Projects")));
const Project = load(lazy(() => import("pages/Project")));
4. Технологии прогрессивных веб-приложений
В прогрессивных веб-приложениях применяются сервис-воркеры. Они позволяют пользователям добавлять приложения на домашние экраны их устройств. Но этим возможности сервис-воркеров не ограничиваются. В частности, они в состоянии значительно улучшить кэширование. Это приводит к тому, что приложение, после первой загрузки, будет загружаться гораздо быстрее.
5. Избавление от пакетов, которые кажутся интересными, но большой пользы не приносят
В моём исходном проекте при монтировании компонентов использовалось много анимации, которая оживляла загрузку страницы. Всё это не просто замедляло страницу. Это делало её гораздо медленнее, чем она могла бы быть. Я, глядя на сайт, и радуясь тому, как симпатично он выглядит, до определённого момента не задумывался о производительности. Но на сайте была не только анимация. Там были и другие подобные украшательства. Например — кнопка, которая позволяла перейти в верхнюю часть страницы. Например — анимированная загрузка некоторых элементов. Всё это мне нравилось, но при ближайшем рассмотрении оказалось, что, например, сайт реально тормозит на не самых быстрых устройствах. Причём, сначала я тестировал сайт исключительно на ноутбуке, поэтому узнал об этом далеко не сразу.
Ещё у меня был компонент-слайдер, который я добавил на страницу без особых размышлений о его «весе». Его я использовал для показа слайд-шоу. Как оказалось позднее, один только код этого слайдера занимал, в минифицированном виде, 30 Кб. Тогда я решил самостоятельно создать компонент для показа слайд-шоу. Его минифицированный код, в итоге, занял 25 КБ. В этот объём входила хорошая библиотека анимации и система работы с жестами, которые можно использовать не только для слайд-шоу, но и в других частях приложения. И выглядит то, что у меня получилось, гораздо лучше, чем стороннее решение.
Вот слайдер из NPM.
Слайдер из NPM
Вот мой слайдер
Слайдер собственной разработки (в жизни он работает гораздо более плавно, чем на этом GIF-изображении с низкой частотой кадров)
Посмотреть этот слайдер в действии можно здесь.
▍Анализ размера бандла
Если вы пользуетесь create-react-app, то вам очень просто проанализировать состав бандла. Для этого выполните команду
npm run build
, а после этого — команду npx source-map-explorer "build/static/js/*.js"
. После этого откроется страница со сведениями о составе бандла, напоминающая ту, что показана ниже.Сведения о бандле
Итоги
Как видите, ускорять React-приложения не так уж и сложно. Достаточно внимательно следить за тем, из чего они построены, тестировать их и вносить в них соответствующие изменения. Вот проект, о котором шла здесь речь, до улучшения, а вот — после.
Если вы интересуетесь оптимизацией React-приложений — вот несколько наших публикаций на эту тему:
- Анализ и оптимизация React-приложений
- Анализ производительности React 16 приложений с помощью инструментов разработчика Chrome
- Кэширование обработчиков событий и улучшение производительности React-приложений
- React, встроенные функции и производительность
Уважаемые читатели! Есть ли у вас примеры удачной (или неудачной) оптимизации React-приложений?