CSS-in-JS занял прочное место в ряду инструментов для фронтенда, и кажется, что эта тенденция сохранится в ближайшем будущем. Особенно в мире React. Например, из 11492 человек, принявших участие в опросе State of CSS в 2020 году, только 14,3% не слышали о Styled Components (доминирующей библиотеке CSS-in-JS). А библиотекой воспользовались более 40% участников.
Я давно хотел увидеть подробное сравнение производительности библиотек CSS-in-JS, таких как Styled Components и старого доброго CSS. К сожалению, мне не удалось найти сравнение на реальном проекте, а не на каком-то простом тестовом сценарии. Поэтому я решил сделать это сам. Я перенес реальное приложение из Styled Components в Linaria, которое будет извлекать CSS во время сборки. Рантайм генерации стилей на компьютере пользователя не будет.
Небольшое замечание, прежде чем мы начнем. Я не ненавижу CSS-in-JS. Я признаю, что у них отличный DX, и подход с применением композиции, берущий начало от React, великолепен. Он может предоставить разработчикам некоторые приятные преимущества, как, например, подчеркивает Джош В. Комо в своей статье The styled-components Happy Path. Я также использую Styled Components в нескольких своих проектах или проектах, над которыми я работал. Но мне было интересно, какова цена этого замечательного DX с точки зрения пользователя.
Посмотрите, что я выяснил.
TL;DR:
Не используйте рантайм CSS-in-JS библиотеки, если вы заботитесь о скорости загрузки вашего сайта. Проще говоря, меньше JS = более быстрый сайт. Мы мало что можем с этим поделать. Но если вы всё же хотите увидеть цифры, продолжайте читать.
Что я измерял и как
Приложение, которое я использовал для теста, - довольно стандартное приложение React, созданное на основе Create React App, Redux с использованием Styled Components (v5) для стилей. Это довольно большое приложение с множеством экранов, настраиваемыми дашбордами, пользовательскими темами и многим другим. Поскольку оно было написано с использованием Create React App, то не производит рендеринг на стороне сервера, поэтому все отображается на клиенте (поскольку это приложение B2B, это не было обязательным требованием).
Я взял это приложение и заменил Styled Components на Linaria, у которого, похоже, есть аналогичный API. Я думал, что перенос будет простым. Оказалось, что всё не так просто. На перенос у меня ушло более двух месяцев, и то, я перенес только несколько страниц, а не все приложение. Думаю, поэтому такого сравнения до сих пор нет 😅. Единственным изменением была замена библиотеки стилей. Все остальное осталось нетронутым.
Я использовал инструменты разработчика Chrome для запуска нескольких тестов на двух наиболее часто используемых страницах. Я всегда запускал тесты трижды, и представленные цифры являются средним значением этих трех прогонов. Для всех тестов я установил CPU throttling в 4x, а network throttling - в Slow 3G. Я использовал отдельный профиль Chrome для тестирования производительности без каких-либо расширений.
Выполненные тесты:
- Network (размер ассетов JS и CSS, coverage, количество запросов)
- Оценки Lighthouse (оценка производительности с мобильным пресетом).
- Performace profiling (тесты загрузки страницы, один тест для взаимодействия с перетаскиванием).
Сравнение Network
Начнем с производительности сети. Одним из преимуществ CSS-in-JS является отсутствие неиспользуемых стилей, верно? Не совсем так. Хотя активны только стили, используемые на странице, вы все равно можете загружать ненужные стили. Но вместо того, чтобы храниться в отдельном файле CSS, они находятся в вашем JS бандле.
Вот сравнение данных той же сборки главной страницы со Styled Components и Linaria. Размер до косой черты - это размер в формате gzip, после неё - размер без сжатия.
![](https://cdn.sanity.io/images/amzoklpq/production/2ba75b22d7d20ce5503fc9906676b8fab69ade4f-616x216.jpg?q=66&auto=format)
![](https://cdn.sanity.io/images/amzoklpq/production/1f4d57bbc84de100373052b96adb8ed0b2018577-617x216.jpg?q=66&auto=format)
Несмотря на то, что размер CSS'а который мы загрузили, значительно увеличился, мы все еще загружаем меньше данных в обоих тестовых сценариях (хотя в этом случае разница почти незаметна). Но что более важно, сумма CSS и JS в Linaria по-прежнему меньше, чем размер самого JS в Styled Component.
Coverage
Если мы сравним этот параметр, то увидим много неиспользуемого CSS в Linaria (около 55 КБ) по сравнению с 6 КБ в Styled Components (это CSS из пакета npm, а не из самих Styled Components). Размер неиспользуемого JS в Linaria на 20 КБ меньше, чем в Styled Components. Но общий размер неиспользуемых аccетов в Linaria больше. Это один из компромиссов внешнего CSS.
![](https://cdn.sanity.io/images/amzoklpq/production/c1d523bf1b64d86cc5ec53c935413e32320c2393-618x127.jpg?q=66&auto=format)
![](https://cdn.sanity.io/images/amzoklpq/production/3ae61cf31c1cc3a4446b6fca9a5648f6d0eaf152-620x125.jpg?q=66&auto=format)
Оценка производительности Lighthouse
Говоря о производительности, было бы упущением не использовать Lighthouse. Вы можете увидеть сравнения на диаграммах ниже (среднее значение из 3 прогонов Linaria). Помимо Web Vitals, я также включил работу основного потока (Main thread) (время на то, чтобы спарсить, скомпилировать и выполнить скрипты, бОльшую часть этого времени занимает JS, но включает и расчет лейаута и стилей, painting и т. д.) и время выполнения JS. Я опустил общий сдвиг лейаута (Cumulative Layout Shift), так как он был близок к нулю, и почти не было разницы между Linaria и Styled Component.
![](https://cdn.sanity.io/images/amzoklpq/production/4177aed761a3a836f1babeb869e1b9e0f9e54484-608x377.webp?q=66&auto=format)
![](https://cdn.sanity.io/images/amzoklpq/production/505c24f374e7de4b0ea7b340d44e2716642989a6-608x377.webp?q=66&auto=format)
Как видите, Linaria быстрее в большинстве Web Vitals (проиграла только однажды в CLS). А иногда и намного быстрее. Например, LCP быстрее на 870 мс на главной странице и на 1,2 с на странице поиска. Страница не только отрисовывается намного быстрее с помощью обычного CSS, но и требует меньше ресурсов. Blocking time (время блокировки) и время, необходимое для выполнения всего JS, меньше на 300 мс и примерно 1,3 секунды соответственно.
Performace profiling
Lighthouse может дать вам много информации о производительности. Но чтобы узнать подробности, лучше всего подойдет вкладка Performance в DevTools. В этом случае вкладка Performance подтверждает результаты Lighthouse. Детали вы можете увидеть на диаграммах ниже.
![](https://cdn.sanity.io/images/amzoklpq/production/03fbc4d7b5ae2f77aa2787b084cb3c2fc2968a82-608x376.webp?q=66&auto=format)
![](https://cdn.sanity.io/images/amzoklpq/production/ae8a38263be2c17d6eaf0dd7156b4bb2ac0409b6-608x376.webp?q=66&auto=format)
Чтобы вам проще было оценить эти данные, вот визуальное сравнение диаграмм производительности для загрузки главной страницы со Styled Components (вверху) и Linaria (внизу).
![](https://cdn.sanity.io/images/amzoklpq/production/3a0166fa517e8884ab768671d1134af3c06258aa-608x262.webp?q=66&auto=format)
Сравнение взаимодействия с пользователем
Важно сравнить также и взаимодействие с пользователем, а не только загрузку страницы. Я измерил производительность перетаскивания, используемого для распределения элементов по группам. Сводка результатов приведена ниже. Даже в этом случае Linaria превзошла рантайм CSS-in-JS в нескольких категориях.
![](https://cdn.sanity.io/images/amzoklpq/production/61fde0006d5d349532d682478df6bd4bdb177b0c-621x155.jpg?q=66&auto=format)
![](https://cdn.sanity.io/images/amzoklpq/production/271a477531ecc231d52a4232cec3c1c37f7dfd2d-608x73.webp?q=66&auto=format)
Заключение
Вот и всё. Как видите, рантайм CSS-in-JS может оказать заметное влияние на веб-страницу. В основном это важно для недорогих устройств и регионов с более медленным Интернет-соединением или более дорогим трафиком. Так что, возможно, нам стоит лучше подумать о том, как и какие именно мы используем инструменты. Удобство для разработчика не должно вредить удобству пользователя.
Я считаю, что мы (разработчики) должны больше думать о влиянии инструментов, которые мы выбираем для наших проектов. В следующий раз, когда я начну новый проект, я больше не буду использовать рантайм CSS-in-JS. Я буду использовать старый добрый CSS или альтернативу CSS-in-JS во время сборки, чтобы получить стили из бандлов JS.
Я думаю, что билдтайм CSS-in-JS библиотеки будут следующим большим шагом в экосистеме CSS, поскольку их появляется все больше и больше (последняя - это vanilla-extract от Seek). И крупные компании тоже идут по этому пути, например, Facebook с их библиотекой стилей.
Спасибо, что прочитали статью. Если у вас есть какие-либо предложения или идеи, как улучшить статью, или она вам просто нравится, не стесняйтесь делиться ею и обсуждать ее в Твиттере.