Фитнес для CSS

Много лет назад я мечтал о временах, когда хороший интернет войдет в каждый дом. И он пришел, а вслед за ним - мобильный интернет. Борьба за колибайтики продолжается.

Чем больше лет проекту, чем больше итераций редизайна он пережил - тем больше его кодовая база. Разбиение CSS на модули, компонентные стили, хорошая архитектура frontend составляющей - если все это прошло мимо вас, дочитайте до конца, возможно мои мысли покажутся вам разумными.

Итак, ваш CSS стал громоздким и вы подозреваете что далеко не весь код реально нужен?

Это произошло и со мной. Я начал искать инструмент, который бы мог показать мне какой CSS код используется в проекте, а какой остался от старых итераций разработки. Первым делом подумалось об uncss от Giacomo Martino. Но, как оказалось, он не очень пригоден для использования на большом проекте с большим количеством страниц, скрытых разделов, авторизациях и прочих штуках, которые зависят от поведения пользователя. Одно дело собрать CSS с нескольких HTML страниц, а совсем другое из работающего интернет магазина с личным кабинетом и внешним видом, зависящем от поведения пользователя.

В uncss использован простой подход - собрать все селекторы и проверить их на существование через document.querySelector. Просто и со вкусом. Но, как повторить все действия пользователя и не пропустить ни одной страницы?

В твиттере Веб-стандартов публиковалась статья Finding Dead CSS Harry Roberts в которой предлагалось маркировать потенциально неиспользуемы CSS код при помощи CSS свойства background-image:

1
2
3
4
#checkout_wrapper {
background-image: url('/assets/img/dead/checkout_wrapper.gif');
// Existing, legacy code
}

и ждать. После определенного времени предлагалось проверить access_log файл на наличие в нем строки, содержащей checkout_wrapper.gif что означало бы тот факт, что CSS селектор жив и используется. В противном случае - селектор следует удалить из CSS.

Очевидно, что такой подход не очень эффективвен при большом количестве CSS, требует много времени, а самое главное - подразумевает тот факт, что нам известно о потенциально неиспользуемых компонентах и блоках. Но сама идея о том, что можно пассивно собирать информацию об используемых CSS селекторах - прекрасна.

А что если соединить идеи uncss и Harry Roberts? Что для этого понадобится?

  • получить все селекторы из всех существующих CSS файлов проекта

  • передать их реальным посетителям проекта

  • обработать селекторы на JS, отфильтровать их, оставив только используемые на странице

  • передать отфильтрованные селекторы на сервер для накопления информации

  • через время обработать существующие CSS на основании накопленной информации

Как получить все селекторы? Работать с CSS как с текстом не удобно, эффективнее получить AST при помощи reworkcss и работать с деревом.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const css = require('css')
const fs = require('fs')
const walker = require('./treeRuleWalker')
const baseDir = 'path-to-your/css/'
const destSelectorFile = 'selectors.json'

const cssRAW = []
const selectors = []

fs.readdirSync(baseDir).forEach(file => {
// получаем в cssRAW содержимое CSS файлов проекта
})

cssRAW.map((el) => {
let { stylesheet: { rules } } = css.parse(el.data)
// обходим дерево и собираем только CSS селекторы
walker(rules, (filtered) => {
selectors.push(...filtered)
}, walker)
})

// сохраняем результат в виде JSON файла

Полученые таким образом селекторы сохраняем в JSON файл и отдаем посетителям сайта. На стороне сайта проверяем существование каждого селектора на странице при помощи document.querySelector, а результат с отфильтрованными селекторами отдаем обратно на сервер и сохраняем (например, в базу данных).

Основную работу выполняет пользователь сайта и его браузер. Нам остается лишь ждать пока накопится достаточное количество данных и на основании этих данных очистить существующие CSS проекта.

В моем случае удалось уменьшить CSS со 156кб до 55кб. При этом данные об используемых CSS селекторах накапливались чуть менее недели.

Proudly powered by Hexo and Theme by Hacker
© 2018 Denis Zavgorodny