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, после неё - размер без сжатия.
Несмотря на то, что размер 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.
Оценка производительности Lighthouse
Говоря о производительности, было бы упущением не использовать Lighthouse. Вы можете увидеть сравнения на диаграммах ниже (среднее значение из 3 прогонов Linaria). Помимо Web Vitals, я также включил работу основного потока (Main thread) (время на то, чтобы спарсить, скомпилировать и выполнить скрипты, бОльшую часть этого времени занимает JS, но включает и расчет лейаута и стилей, painting и т. д.) и время выполнения JS. Я опустил общий сдвиг лейаута (Cumulative Layout Shift), так как он был близок к нулю, и почти не было разницы между Linaria и Styled Component.
Как видите, Linaria быстрее в большинстве Web Vitals (проиграла только однажды в CLS). А иногда и намного быстрее. Например, LCP быстрее на 870 мс на главной странице и на 1,2 с на странице поиска. Страница не только отрисовывается намного быстрее с помощью обычного CSS, но и требует меньше ресурсов. Blocking time (время блокировки) и время, необходимое для выполнения всего JS, меньше на 300 мс и примерно 1,3 секунды соответственно.
Performace profiling
Lighthouse может дать вам много информации о производительности. Но чтобы узнать подробности, лучше всего подойдет вкладка Performance в DevTools. В этом случае вкладка Performance подтверждает результаты Lighthouse. Детали вы можете увидеть на диаграммах ниже.
Чтобы вам проще было оценить эти данные, вот визуальное сравнение диаграмм производительности для загрузки главной страницы со Styled Components (вверху) и Linaria (внизу).
Сравнение взаимодействия с пользователем
Важно сравнить также и взаимодействие с пользователем, а не только загрузку страницы. Я измерил производительность перетаскивания, используемого для распределения элементов по группам. Сводка результатов приведена ниже. Даже в этом случае Linaria превзошла рантайм CSS-in-JS в нескольких категориях.
Заключение
Вот и всё. Как видите, рантайм CSS-in-JS может оказать заметное влияние на веб-страницу. В основном это важно для недорогих устройств и регионов с более медленным Интернет-соединением или более дорогим трафиком. Так что, возможно, нам стоит лучше подумать о том, как и какие именно мы используем инструменты. Удобство для разработчика не должно вредить удобству пользователя.
Я считаю, что мы (разработчики) должны больше думать о влиянии инструментов, которые мы выбираем для наших проектов. В следующий раз, когда я начну новый проект, я больше не буду использовать рантайм CSS-in-JS. Я буду использовать старый добрый CSS или альтернативу CSS-in-JS во время сборки, чтобы получить стили из бандлов JS.
Я думаю, что билдтайм CSS-in-JS библиотеки будут следующим большим шагом в экосистеме CSS, поскольку их появляется все больше и больше (последняя - это vanilla-extract от Seek). И крупные компании тоже идут по этому пути, например, Facebook с их библиотекой стилей.
Спасибо, что прочитали статью. Если у вас есть какие-либо предложения или идеи, как улучшить статью, или она вам просто нравится, не стесняйтесь делиться ею и обсуждать ее в Твиттере.