Уменьшаем CSS лениво

Денис Завгородний

WSD #2018 Kiyv

Привет, WSD!

ChernivtsiJS
ChernivtsiJS

Уменьшаем CSS
*лениво

Что имеем?

  1. беспорядочный проект
  2. хаотичный редизайн
  3. нет организации CSS кода
  4. слабая структурированность

Что мы хотим?

  1. быструю загрузку
  2. быструю отрисовку
  3. красивый код
  4. модульность
  5. постоянный рефакторинг

Что для этого нужно?

  1. переделать все
  2. уменьшить размер CSS
  3. оптимизировать загрузку ресурсов
  4. разделить CSS на логические части
  5. много трудиться

Очистить CSS

Инструменты и практики

UnCSS

UnCSS

npm install uncss

UnCSS

            const uncss = require('uncss');
            const files = ['index.html', 'https://google.com'];
            uncss(files, function (error, clearCSS) {
                console.log(clearCSS);
            });
        

UnCSS | Преимущества

UnCSS | Недостатки

uCSS

uCSS

npm install ucss

uCSS | Выводы

...

Helium CSS

Helium CSS

            <script src="helium.js"></script>
            <script type="text/javascript">
                window.addEventListener('load', function(){
                    helium.init();
                }, false);
            </script>
        

Helium CSS | Выводы

Finding Dead CSS

Как мы узнаем о том, что CSS код мертв? Несмотря на то, что инструменты вроде uncss очень мощные, они не совсем подходят под наши требования. А что, если нам нужно решение в основу которого положен принцип мониторинга реальных пользователей, который позволяет увидеть какой код действительно используется у реальных пользователей на реальном сайте?

Harry Roberts, ссылка на статью

Finding Dead CSS | Реализация

            .some .selector {
                background-image: url('/img/dead/some_selector.gif');
                color: red;
                padding: 0;
            }
        

Finding Dead CSS | Идея

Finding Dead CSS | Анализ

RUM
Real User Monitoring

Требования

Идея

Инструменты

Требования к парсеру в AST

С чем работаем?

Rule | Правило

                        .title-search-item :hover {
                            /* declarations */
                            prop: value;
                            ...
                        }
                    

At-rule | Директива

                    @media screen and (max-width: 1200px) {
                        /* rules */
                        ...
                    }
                

Работаем с правилами и селекторами

                .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

Rework устарел

CSSTree

            const csstree = require('css-tree');
            ...
            var ast = csstree.parse(cssString);
            csstree.walk(ast, function(node) {
                ...
            });
            //csstree.generate(ast)
        

CSSTree

PostCSS

            const postcss = require('postcss');
            const proc = postcss((ast) => {
                ast.walkRules(/^\D/, (rule) => {
                    let { selector } = rule;
                    ...
                });
            }).process(cssString).then(result => {...});
        

PostCSS

Получаем список селекторов

            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",
                ...
            ]

        

Что дальше?

Что дальше?

Что-то забыли?

Событие

Событие

Где хранить?

Где хранить?

Свой сервер с БД

Google Firebase

Результат в JSON

            {
                "url" : "/index.html",
                "data" : [
                    ".rightcol-profile .button-cancel",
                    ".cart-step-3 .prb span",
                    ".selector1", ".selector2", ".selector3"
                    ...
                ]
            }
        

Очищаем

Схема работы

На сервере

На клиенте

            <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>
        

Подготовка

Результат

ссылка на GitHub

Спасибо!

Denis Zavgorodny
web technologist, founder of @ChernivtsiJS,
co-founder @NodeSchool Chernivtsi

@DenisZavgorodny

denis.zavgorodny@gmail.com