Привет, WSD!
Что имеем?
- беспорядочный проект
- хаотичный редизайн
- нет организации CSS кода
- слабая структурированность
Что мы хотим?
- быструю загрузку
- быструю отрисовку
- красивый код
- модульность
- постоянный рефакторинг
Что для этого нужно?
переделать все
- уменьшить размер CSS
- оптимизировать загрузку ресурсов
- разделить CSS на логические части
- много трудиться
Инструменты и практики
UnCSS
- анализирует статические файлы или URL
JSDOM
для эмуляции работы браузера
PostCSS
для парсинга CSS
npm install uncss
UnCSS
const uncss = require('uncss');
const files = ['index.html', 'https://google.com'];
uncss(files, function (error, clearCSS) {
console.log(clearCSS);
});
UnCSS | Преимущества
- можно использовать в системах сборки: gulp, grunt, broccoli
- есть online версия https://uncss-online.com/
- умеет анализировать
CSS
, добавленный на JS
- использует
JSDOM
, не требует запуска браузера
- отлично подходит для статических сайтов
UnCSS | Недостатки
- проводит анализ стилей только по событию "
onload
"
- не умеет обрабатывать изменения
DOM
- не умеет обрабатывать закрытые разделы
- отлично подходит для статических сайтов
uCSS
- анализирует статические файлы или URL
Cheerio
для работы с DOM
CSSOM
для парсинга CSS
npm install ucss
uCSS | Выводы
- преимущества и недостатки как у
UnCSS
- не нашел интеграции с системами сборки
- не поддерживается, не было коммитов 2 года
- нет online версии
Helium CSS
<script src="helium.js"></script>
<script type="text/javascript">
window.addEventListener('load', function(){
helium.init();
}, false);
</script>
Helium CSS | Выводы
- исполняется в браузере
- сложно встроить в линию сборки
- предоставляет информацию, но не понятно что с ней делать
- не поддерживается, не было коммитов 4 года
Finding Dead CSS
Finding Dead CSS | Реализация
.some .selector {
background-image: url('/img/dead/some_selector.gif');
color: red;
padding: 0;
}
Finding Dead CSS | Идея
- помечаем селекторы
- включаем access логи сервера
- накапливаем информацию
- все что попало в access логи – живо
Finding Dead CSS | Анализ
- дополнительные ресурсы у страницы
- нужно вручную пометить весь CSS код
- сложно анализировать access логи
- GIF можно не создавать и сделать rewrite на сервере
- пользователи сами накапливают статистику
Требования
- анализ существующего CSS
- автономный сбор и хранение данных
- анализ динамического контента:
- модальные окна
- отложенно загружаемый контент
- повторное использование с тем же результатом
Идея
- собрать существующие селекторы
- протестировать на актуальность силами посетителей
- накопить результат
- очистить CSS
Инструменты
- парсер AST для CSS
- временное хранилище данных на клиенте
- постоянное хранилище данных на сервере
Требования к парсеру в AST
- нет требований к производительности
- должен обрабатывать комментарии
- простой и с хорошей документацией
С чем работаем?
Rule | Правило
.title-search-item :hover {
prop: value;
...
}
At-rule | Директива
@media screen and (max-width: 1200px) {
...
}
Работаем с правилами и селекторами
.title-search-item { ... }
.title-search-item a:hover { ... }
.title-search-item :hover { ... }
.cart-amount__title::after { ... }
Отбрасываем псевдоклассы и псевдоэлементы /(>?\s?:[\S]+)/g
Парсер в AST
|
|
|
Rework CSS parser |
CSSTree |
PostCSS |
Rework CSS parser
const parser = require('css')
...
var { stylesheet: { rules } } = parser.parse(cssString)
walker(rules, (filtered) => {
...
}, walker);
Rework CSS parser
- работает
- комментарии входят в AST как отдельный тип правил
- нет собственного метода обхода дерева
- коммит 4 года назад
Rework устарел
CSSTree
const csstree = require('css-tree');
...
var ast = csstree.parse(cssString);
csstree.walk(ast, function(node) {
...
});
CSSTree
- быстрый
- современный
- отличная документация и API
- отбрасываются комментарии
- отбрасываются пробелы, теряется форматирование
PostCSS
const postcss = require('postcss');
const proc = postcss((ast) => {
ast.walkRules(/^\D/, (rule) => {
let { selector } = rule;
...
});
}).process(cssString).then(result => {...});
PostCSS
- быстрый
- современный
- отличная документация и API
- можно сохранить форматирование
Получаем список селекторов
ast.walkRules(/^\D/, (rule) => {
let { selector } = rule;
selector = selector.replace(/\n/g, '');
selectors.push(...selector.split(','));
});
Получаем список селекторов
[
".rightcol-profile .button-cancel",
".cart-step-3 .prb span",
"#block-analogues-popup .prb span",
".cart-step-3 .prb.old::after",
...
]
Что-то забыли?
- модальные окна
- состояния UI компонентов
- рендер компонентов на стороне клиента
Событие
- использовали
DOM EventListener
- добавили генерацию события для случаев изменения DOM
- модифицировали JS код проекта
Свой сервер с БД
- он обычно есть
- легко интегрировать (например, POST запрос с клиента)
- дополнительная нагрузка на сервер
Google Firebase
- есть бесплатный аккаунт, не требует дополнительных расходов
- не нагружается основная база и сервер
- относительная легкость в интеграции
- тяжеловесная клиентская библиотека (~ 100KB, gzip)
Результат в JSON
{
"url" : "/index.html",
"data" : [
".rightcol-profile .button-cancel",
".cart-step-3 .prb span",
".selector1", ".selector2", ".selector3"
...
]
}
Схема работы
На сервере
- создаем индекс CSS селекторов
cssclear index ./css path/to/selectors.json
- ...
- очищаем CSS файлы
cssclear clear -f path/to/live.json ./css ./dest
На клиенте
<script>
var cssClear = {
pathToSelectors: 'https://yourdomain.com/selectors.json',
dataStoreProvider: {
name: 'postData',
options: {
apiKey: "d58e3582afa99040e27b92b13c8f2280",
URL: 'https://apidomain.com/save/point/'
}
}
};
</script>
<script async src="path/to/csscleaner.bundle.js"></script>
Подготовка
- тестирование
- E2E тесты
- тесты CSS регрессий
- мониторинг метрик загрузки сайта
Результат
- сократили CSS код в 3 раза
365кб не сжатого CSS против ~1200кб
- получили небольшой прирост в Google Insights
- получили инструмент для повторного использования
- получили удовольствие
ссылка на GitHub