Долгие годы шли споры, как лучше именовать классы - по бэму, по целям, по компонентам или как угодно, но с добавлением хеша. И это действительно важный вопрос, какой способ будет комфортен в разработке большого и развивающегося проекта. Но, что эти способы означают для пользователя, нужны ли ему эти классы и как они связанны с его опытом?
Порою, заходя в стили проектов, я невольно пугаюсь сложившейся длине имён - модуль, блок, элемент, подэлемент, модификатор 1, модификатор 2. БЭМ действительно великолепен и я не собираюсь его отрицать, но его размеры оставляют желать лучшего.
Длинные классы увеличивают вес страницы, это в свою очередь означает увеличение времени загрузки самого главного для рендера страницы - документа и файла стилей, от которых напрямую зависят метрики FCP, LCP.
Это стало одной из причин почему я долгое время засматриваюсь на модули (в дополнение к изоляции стилей и их хранении там, где они используется).
Модули дают возможность называть классы короче, только под текущий компонент, сохраняя при этом удобство разработки. Но теперь к классам добавляются хеши, делая их длиннее, а значит преимущество не такое, как хотелось бы. Поэтому, наконец, к теме статьи.
Сжатие имён классов
Итак, какие есть методы сократить классы:
Называть классы короче (Спасибо, Кэп, да);
Называть классы полноценно, но при сборке оставлять только хеш;
Называть имена по определённому правилу или алгоритму.
Первый способ не подходит для чего-то большего, чем to-do лист - сделав классы слишком короткими мы либо теряем DX, либо рискуем сделать пересечения.
Для второго и третьего же способа, css-loader предлагает свойство localIdentName
для модулей.
localIdentName: "[path][name]__[local]--[hash:base64:5]"
localIdentName: "[hash:base64]"
Самое оптимальное сжатие
Подобрав правило можно значительно сократить размер классов, но всё же, значительно не равно максимально. Максимальным уменьшением имён классов будет сохращение до символов - .a
, .b
, .c
, …, etc.
Таким подходом пользуются, например, Google, Facebook, Instagram
Чтобы реализовать такое решение нас интересует свойство getLocalIdent
, которое позволяет передавать функцию для генерирования имени. Также можно использовать такие пакеты как posthtml-minify-classnames или mangle-css-class-webpack-plugin.
На этом можно было бы завершать статью, если бы не одна деталь. Я использую next.js.
Решение
Next.js имеет несколько особенностей, которые не позволяют использовать эти решения. Самой очевидной особенностью является то, что он не даёт возможности настроить getLocalIdent снаружи.
Именно поэтому я, 3 года назад сделал пакет - next-classnames-minifier
. В нём я реализовал алгоритм подбора имён и настроил встраивание getLocalIdent
в нужные правила в вебпаке. За последующие годы пакет незначительно обновлялся, но было в нём то, что не позволяло мне назвать его завершённым и готовым для использования в коммерческих проектах.
Главной проблемой была необходимость каждый раз удалять папку собранного приложения и кеша в ci, что, безусловно, очень сказывалось на удобстве разработки. И в этом вторая особенность Некста - его система кеширования.
Если компонент собрался, то при следующем запуске режима разработки или сборки - он может не пересобираться. То есть при перезапуске, алгоритм начинал работать с чистого листа и генерировал классы с самых простых имён (.a
, .b
, .c
), но в ряде непересобираемых компонент и стилей такие имена были добавлены при прошлом запуске.
По этой причине решение и не включено в next.js
Подружиться с Next.js
Очевидно, критично важной задачей было избавить от проблемы очистки кеша. И решение нашлось. Теперь пакет, подобно next.js, кеширует результаты - сгенерированные имена - и при каждом запуске восстанавливает их из кеша, анализируя их и проверяя их актуальность.
При этом, скорость сборки не стала дольше, ведь пакет использует всё тотже оптимизированный алгоритм подбора имени, а за счёт кеширования пакет работает ещё быстрее [чем базовое создание имён с генерированием хеша].
Эффективность
Можно найти статьи с эффективностью сжатия 30%, 50% и даже 70%. В реальности же всё очень индивидуально. Например, если у вас был класс:
.postsListItemTitle {
font-size: 24px;
}
Из него получится:
.j1 {
font-size: 24px;
}
21 символ (.j1{font-size: 24px;}
) вместо 44 (.postsListItemTitle__h53_j{font-size: 24px;}
) - экономия 52%. Этот класс используется в 20 карточках на странице, что уменьшает вес и html.
В среднем же, пожалуй, можно говорить о уменьшении веса css на 10-20%.
next-classnames-minifier - давайте сделаем веб Ещё быстрее.