От автора: удаление неиспользуемых стилей CSS в ряде случаев бывает просто необходимым. Я большой поклонник utility-first CSS. После ряда экспериментов на протяжении многих лет, это то, что я нашел самым лучшим, самым удобным и масштабируемым способом написания CSS на сегодняшний день.
Когда мой коллега Клемент Денуа и я построили api-search.io, я решил использовать для его стилизации Tailwind CSS, полностью настраиваемую utility-first библиотеку.
Весь смысл библиотеки — предоставить вам доступ к широкому набору инструментов для использования по своему усмотрению. Проблема в том, что, поскольку вы обычно используете только ее подмножество, в конечном итоге вы получаете множество неиспользуемых правил CSS.
В моем случае я не только загружал всю библиотеку Tailwind CSS, но также добавлял несколько вариантов для некоторых модулей. Это привело к тому, что окончательный минимизированный CSS-файл имел размер 259 КБ (до GZip). Это довольно много, если учесть, что веб-сайт представляет собой простое одностраничное приложение с минимальным дизайном.
Вы не хотите загружать каждую утилиту вручную, когда она вам нужна. Это была бы продолжительная и громоздкая работа. Лучшее решение — иметь все в своем распоряжении во время разработки и автоматически удалять то, что вы не использовали на этапе сборки.
В JavaScript мы называем это tree-shaking. Теперь, благодаря PurgeCSS, вы можете сделать то же самое с CSS.
PurgeCSS анализирует файлы контента и CSS, а затем сопоставляет селекторы. Если он не обнаруживает вхождения какого-либо селектора в контенте, он удаляет его из файла CSS. По большей части это может работать из коробки. Тем не менее, на любом веб-сайте есть некоторые области, для которых может потребоваться дополнительный анализ, прежде чем мы позволим PurgeCSS выполнить магию.
Разделение CSS
Проект содержит три основных файла CSS:
Сброс CSS, называемый normalize.css, включенный в Tailwind CSS.
Tailwind CSS, самая существенная часть моей базы кода CSS.
Некоторые пользовательские стили CSS, в основном для стилизации компонентов InstantSearch, которым я не мог добавлять классы.
PurgeCSS не может обнаружить, что мне нужно сохранить такие селекторы, как .ais-Highlight, потому что компоненты, которые его используют, отображаются только в DOM во время выполнения. То же самое касается normalize.css — я полагаюсь на него, чтобы сбросить стили браузера, но многие из связанных компонентов никогда не будут сопоставлены, потому что они созданы в JavaScript.
В случае классов, начинающихся с .ais-, мы можем сортировать их с помощью белого списка. Но когда дело доходит до сброса стилей, селекторы отслеживать немного сложнее. Кроме того, размер normalize.css довольно незначителен и его не обязательно изменять, поэтому в данном случае я решил полностью проигнорировать этот файл. Следовательно, перед запуском PurgeCSS мне пришлось разделить стили.
Моя первоначальная конфигурация CSS выглядела так:
Файл tailwind.src.css с тремя директивами @tailwind: preflight, components и utilities.
Файл App.css с пользовательскими стилями.
Скрипт npm в package.json для создания Tailwind CSS непосредственно перед запуском или созданием проекта. Каждый раз, когда запускается этот скрипт, он выводит в src файл tailwind.css, который загружается в проект.
Директива @tailwind preflight загружает normalize.css. Я не хотел, чтобы PurgeCSS касался его, поэтому я переместил его в отдельный файл.
1 2 3 4 5 6 7 |
// tailwind.src.css @tailwind components; @tailwind utilities; /* normalize.src.css */ @tailwind preflight; |
Затем я изменил существующий скрипт в пакете package.json для отдельной сборки normalize.src.css.
1 2 3 4 5 6 7 |
{ "scripts": { "tailwind": "npm run tailwind:normalize && npm run tailwind:css", "tailwind:normalize": "tailwind build src/normalize.src.css -c tailwind.js -o src/normalize.css", "tailwind:css": "tailwind build src/tailwind.src.css -c tailwind.js -o src/tailwind.css" } } |
Наконец, я загрузил normalize.css в проект.
1 2 3 4 5 6 7 |
// src/index.js ... import './normalize.css' import './tailwind.css' import App from './App' ... |
Теперь я могу запустить PurgeCSS для tailwind.css, не опасаясь, что он удалит необходимые правила.
Настройка PurgeCSS
PurgeCSS поставляется во многих вариантах: интерфейс командной строки, API JavaScript, оболочка для Webpack, Gulp, Rollup и т. д.
Мы использовали для загрузки сайта Create React App, поэтому Webpack был предварительно сконфигурирован и скрыт за react-scripts. Это означает, что я не могу получить доступ к файлам конфигурации Webpack, если только я не запустил npm run eject, чтобы вернуть их и управлять ими непосредственно в проекте.
Отсутствие возможности управлять Webpack дает вам много преимуществ, поэтому eject не было вариантом. Вместо этого я решил использовать пользовательский файл конфигурации для PurgeCSS и скрипт npm.
Сначала я создал в корне проекта purgecss.config.js:
1 2 3 4 |
module.exports = { content: ['src/App.js'], css: ['src/tailwind.css'] } |
Свойство content принимает массив файлов для анализа в соответствии с селекторами CSS.
Свойство css принимает массив таблиц стилей для очистки.
Затем я отредактировал скрипты npm для запуска PurgeCSS:
1 2 3 4 5 6 7 8 |
{ "scripts": { "start": "npm run css && react-scripts start", "build": "npm run css && react-scripts build", "css": "npm run tailwind && npm run purgecss", "purgecss": "purgecss -c purgecss.config.js -o src" } } |
Я добавил скрипт purgecss, который принимает файл конфигурации и выводит очищенную таблицу стилей в src. Я запускаю этот скрипт каждый раз, когда мы начинаем или создаем проект.
Специальный экстрактор для Tailwind CSS
Tailwind CSS применяет специальные символы, поэтому, если вы используете PurgeCSS из коробки, он может удалить необходимые селекторы. К счастью, PurgeCSS позволяет использовать пользовательский экстрактор, который является функцией, в которой перечислены селекторы, используемые в файле. Для Tailwind мне нужно было создать такой пользовательский экстрактор:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
module.exports = { ... extractors: [ { extractor: class { static extract(content) { return content.match(/[A-z0-9-:\/]+/g) || [] }, extensions: ['js'] } } ] } |
Белый список классов, генерируемых во время выполнения
PurgeCSS не может обнаруживать классы, которые генерируются во время выполнения, но позволяет определять белый список. Классы, выбранные в белый список, остаются в конечном файле независимо ни от чего.
В проекте используется React InstantSearch, который генерирует компоненты с классами, все они начинаются с ais-. Это удобно, так как PurgeCSS поддерживает шаблоны в виде регулярных выражений.
1 2 3 4 5 6 |
module.exports = { ... css: ['src/tailwind.css', 'src/App.css'], whitelistPatterns: [/ais-.*/], ... } |
Теперь, если я забуду удалить класс, который я больше не буду использовать из App.css, он будет выведен из окончательной сборки, но мои селекторы InstantSearch останутся.
Новая сборка, более компактный CSS
С этой новой конфигурацией мой последний файл CSS уменьшился с 259 КБ до … 9 КБ! Это очень важно в контексте всего проекта, тем более, что во многих странах по-прежнему наблюдается медленный и нестабильный Интернет, и все больше людей просматривают приложения на телефоне во время движения.
Доступность также касается доставки контента людям, пользующихся каналом с низким уровнем пропускной способности. Если не пытаться помочь своим пользователям с более медленным Интернетом, это неприемлемо. Особенно если это касается мертвого кода. Стоит потратить немного времени на то, чтобы оптимизировать сборку.
Автор: Sarah Dayan
Источник: //frontstuff.io/
Редакция: Команда webformyself.