Андрей Ситник: «Будущее за CSS постпроцессорами!»

Всем привет! Редакция Code Hipsters поговорила с фронтендером Evil Martians и автором PostCSS Андреем Ситником о пре- и пост-процессинге CSS, трендах фронтент-разработки и планах на будущее.

Code Hipsters: Добрый день, Андрей! Если коротко, то зачем пересобирать CSS-ки? Все же давно и надолго радуются Sass и Less.

Андрей Ситник: Кому нужна пост-обработка — формально всем фронтенд-разработчикам. PostCSS служит как замена Sass, LESS, Stylus. Кроме этого он нужен для линтеров, минификаторов, плагинов для редакторов. В общем, если тебе нужно как-то изменить CSS, то проще всего это сделать через PostCSS, так как он даст тебе карты кода, API, и парсер. Теперь вопрос, чем лучше Sass и т.д. Собственно, первую идею постпроцессора предложил TJ и сделал свой Rework, вот тут он пишет зачем. Если вкратце: препроцессоры сложные и медленные и дают мало функций. Теперь подробнее. Сложные, потому что они монолитные. Есть только один тип переменных, встроенный в язык. Есть парсер особого языка программирования прямо в CSS, всё это усложняет разработку препроцессоров. Очень мало кто коммитил в Sass, потому что это трудно. PostCSS — это просто парсер и стрингифаер. Все функции (переменные, примеси, префиксы) — это подключаемые JS-модули. Много кто разрабатывает такие плагины, потому что это очень просто.

Во-вторых, PostCSS не имет какого-то языка программирования внутри CSS. Вся магия (например, разворачивание префиксов) написана в JS-коде. В плагине Автопрефиксер нет @if, @for и т.п. Можно написать плагин, который будет это добавлять, но тьюринг-полного языка конструкций нет и вряд ли будет, так как проще написать узкозаточенный под бизнес-логику плагин на JS, чем писать вычисления «на Sass». Одна из реализаций переменных предложена здесь (это на самом деле полифил к новой спеке CSS-переменных).

Code Hipsters: А как мы получаем переменные и прочие миксины? То есть как это делается с предобработкой мне понятно — вот текст, вот парсер, перевели — и молодцы. Верно ли, что мы скармливаем постпроцессору произвольный текст, а перевод делают JS-модули?

Андрей Ситник: Поскольку там есть конкретный парсер, то текст должен быть похож на CSS.

Code Hipsters: Ок, про произвольный я специально перегнул.

Андрей Ситник: Угу, но да, он может быть расширен какими-то кастомными конструкциями в широких пределах, а какой-то конкретный плагин будет эти конструкции ловить. Можно написать поддержку @if, например: достаточно в плагине найти все at-rule с name == "if" и что-то сделать с их телом.

Code Hipsters: А если несколько плагинов одни at-rule ловят? Насколько большие проблемы доставляет такой произвол?

Андрей Ситник: Тут вопрос в порядке подключения плагинов: например, переменные желательно подключать до calc().

Code Hipsters: А тестировать это как-то можно?

Андрей Ситник: Пока баг-репрортов с коллизиями не было, так что такой вопрос не поднимался; обычно плагинов до 10.

Code Hipsters: А сколько всего успели написать? И много ли ребят кроме тебя этим занимаются?

Андрей Ситник: Практически все плагины перечислены тут. В основном работают 2 француза, один японец, necolas из Твиттера. Француз, например, написал cssnext-playground, японец написал минификатор - один из немногих кто держит карты кода, Николас линтер) написал, он сейчас переходит с Rework на PostCSS. В целом, Твиттер сейчас на постпроцессорах, правда, на Rework, он был первым.

Code Hipsters: А ты насколько активно занимаешься проектом сейчас?

Андрей Ситник: Я бы сказал, что моей компании очень не нравится, насколько активно я занимаюсь проектом, в том числе в рабочее время. Мы закончили пока с первым плюсом PostCSS — они легче в разработке, чем Sass; дальше — скорость. Из-за монолитности и поддержки тьюринг-полного языка в Sass они очень медленные, Ruby Sass непозволительно медленный. Libsass решает проблему, но урезает функционал (нельзя подключать Ruby-расширения), в итоге спрайты и инлайн картинок уже нормально не сделать. Stylus быстрее, но не сильно. Из-за простоты постпроцессоры очень быстрые. По бенчмаркам cssnext на PostCSS в 3 раза быстрее libsass и в 30 раз быстрее Ruby Sass (бенчмарк).

Code Hipsters: Именно за счет другой модели работы, верно?

Андрей Ситник: Ага. Просто парсер и пару JS-функций, бегающих по дереву. Конечно всё ещё зависит от того, насколько сложные эти функции, но сейчас все они быстрее Stylus точно. Кстати, покажу ещё крутой процессор — полифил для filter: blur(2px) и т. п.

Ну и третий плюс — возможности.

Code Hipsters: К слову, а почему пост-?

Андрей Ситник: Вот тут большая непонятка, я так и не понял, откуда появилось это слово. Думал его придумал TJ, но оказалось, что он его не упоминал. Главная идея названия в том, что постпроцессоры работают уже с CSS, хотя иногда это нарушается.

Code Hipsters: Но это же не CSS! Я запутался.

Андрей Ситник: Есть проблема с названием, я бы сказал что оно историческое.

Code Hipsters: Понял.

Андрей Ситник: Третий плюс про мощность. Проблема Sass в том, что всю магию можно сделать только на примесях, переменных или функциях. Единственный способ сделать префиксы — добавить свою библиотеку примесей. Но что если я не хочу помнить о том, где нужно использовать примесь, а где нет? У Автопрефиксера очень крутой UI — ты просто забываешь о префиксах, такого UI нельзя сделать в препроцессорах. То же самое с полифилами — хочется, чтобы они просто «магически» работали.

Code Hipsters: Потому что препроцессоры толком нельзя расширять? Из-за сложного ядра?

Андрей Ситник: Я бы сказал, ограничение в самой идеи. Sass — это примеси, переменные и функции, сама идея не позволяет свободно ходить по AST.

Code Hipsters: По факту, один из основных плюсов PostCSS — это контроль и маленькие обработчики для конкретных задач?

Андрей Ситник: Ну и гибкость.

Code Hipsters: Такие Web Components для CSS.

Андрей Ситник: Я бы сказал, компоненты для процессинга CSS. Но да, ты собираешь свою обработку из кирпичиков, кирпичики простые, могут делать что угодно и быстрые. Вот ещё пример, что нельзя сделать на примесях или функциях: полифил для rem. Или плагин, который выносит все заинлайненные картинки в отдельный файл (чтобы стили грузились в два этапа). Полифилов очень много, я хочу выходить из гнёта спек и делать какие-то новые вещи. Например, реализовать переменные как в Sass, так как они удобнее спеки.

Code Hipsters: JavaScript пытаются починить куча людей, насобирали языков и линтеров, все никак не починят, как многим кажется.

Андрей Ситник: JavaScript ужасный язык, на котором мы вынуждены программировать.

Code Hipsters: А зачем столько сил тратить на CSS?

Андрей Ситник: Потому что CSS кода больше.

Code Hipsters: То есть на него сильно сложные задачи ложатся? Но он же простой?

Андрей Ситник: В проекте CSS кода может быть больше чем всего остального, когда его очень много, он не поддерживаемый. Поэтому мы меняем простоту на сокращение размера и увеличение поддерживаемости.

Code Hipsters: Так и less такой же не поддерживаемый.

Андрей Ситник: Ну не совсем, переменные и примеси уже позволили сделать его лучше, например хаки вынести. То есть ты пишешь не 5—6 правил, тут же забывая, зачем они, а пишешь +clearfix. Кстати, много логики из JS начинает идти в CSS, например анимации; я вообще стараюсь в JS только классы менять, а все анимации и внешний вид состояний описывать в CSS.

Code Hipsters: А как насчет стилизации без классов, насколько это реально? Я недавно на статью о селекторе «лоботомированная сова» наткнулся, писали о нем где-то неделю назад.

Андрей Ситник: Есть ещё другая идея — инлайн всех стилей в теги, ReactStyle этим занимается.

Code Hipsters: Зачем? Т.е. один запрос а не два, быстрее обработка, потому что все уже инлайн?

Андрей Ситник: Ну это ребята из React угорают.

Code Hipsters: Но это же плохо?

Андрей Ситник: Но там есть интересные идеи, пока рано рассматривать серьёзно и надо смотреть на время рендера. Ну в общем CSS так же активно развивается, так как это такой же код, как и JS, пусть и не алгоритм. Нам всё равно надо его красиво организовывать и иметь инструменты.

Code Hipsters: Резюмируя, процессинг нужен, потому что язык не позволяет писать поддерживаемый код, плюс, очень много всего уходит из ведения jQuery-animation в CSS.

Андрей Ситник: Я бы сказал, что переход jQuery-анимаций в CSS сразу же сокращает код. Анимациям не место в JS идеологически, отсюда получаем практические проблемы: падение скорости, смесь представления и логики и т.п. Кстати, пока PostCSS-плагинов для анимаций не было, а жаль. Надо, наверное, написать чистилку CSS-анимаций, которых никто не будет использовать.

Code Hipsters: А unCSS этого не делает?

Андрей Ситник: Может и делает, но unCSS сложно подключить во многие проекты, где нельзя быстро получить весь возможный HTML, а с анимациями мы знаем точно, если договорились не менять стили руками из JS. Так что unCSS мы пока не используем. Мне кажется, эту проблему лучше решать компонентным подходом, чтобы JS, шаблон и CSS лежали в одной папке — удалил компонент, удалил и стили.

Code Hipsters: А React?

Андрей Ситник: А что с React?

Code Hipsters: Вьюхи в непонятном формате, и из них разметка по волшебству получается.

Андрей Ситник: Есть у меня одна идейка — взять это и объединить с react-haml. Но пока ещё только в формате идеи.

Code Hipsters: К слову, раз речь зашла. Как ты думаешь, почему по React-у все так угорели?

Андрей Ситник:

  1. Он очень быстро рендерит.
  2. Он решает многие проблемы 2-way data binding с помощью реактивного подхода. Если мне нужно будет делать SPA (я правда считаю, что большинству проектов SPA не нужен), то я буду делать на React-е.

Code Hipsters: Так сейчас каждый первый фреймворк реактивный.

Андрей Ситник: Ну Ember и Angular вроде ещё нет.

Code Hipsters: К слову, Ember обещают впилить react-way рендеринг ко второй версии, т.е., довольно скоро.

Так Angular все сам обновляет, по сути, как я понимаю, та же реактивность. По крайней мере в том смысле, что если поменять переменную, от которой зависит другая, то и последняя поменяется.

Андрей Ситник: Ну до FLUX им далеко в плане реактивности. То есть вся идея в том, что у нас есть модели, события, на выходе получаем JSON-состояния, и оно попадает в state-less вьюшки на React — она довольно сильная.

Code Hipsters: Потому что с 2-way все намучались?

Андрей Ситник: Именно, что React-компоненты без состояний. Я бы сказал, что есть сайд-эффекты. По фреймворкам лучше спроси Лёшу Плуталова (у нас в марсианах), он к курсу очень глубоко погрузился в каждый: Ember и Angular, я их знаю только поверхостно.

Code Hipsters: Просто к слову пришлось, я поэтому спросил. Про React интересно, потому что, как говорил, мне пока непонятно, почему такой сильный хайп.

Андрей Ситник: Простота — нет $scope, watch.

Code Hipsters: И провайдеров для конфигурации сервисов, которые встраиваются в контроллеры директив. Видимо, даже отдельный формат для разметки после этого спагетти уже не пугает. И отдельной сторокой, почему React от Facebook такой быстрый, а Facebook — нет?

Андрей Ситник: Это хороший вопрос :D

Code Hipsters: Про инструменты хотел спросить. По ссылкам, которые ты кидал, есть и grunt-, и gulp-плагины; есть ли у PostCSS «любимая» система сборки? Насколько сложно вообще встраивать обработчики в процесс подготовки проекта к деплою?

Андрей Ситник: Я какое-то время увлекался webpack, так как он сейчас самый умный. Поэтому единственный плагин для поддержки PostCSS, что лежит в огранизации PostCSS — это postcss-loader для вебпака. Но последнее время я сильно расстроился из-за webpack. Параметры в плагины сложно передавать (только URL-строкой). Поэтому PostCSS-loader использует хак с паками. И Webpack ужасно работает с CSS.

Code Hipsters: А как же grunt и gulp? Все с первого на второй только переезжают.

Андрей Ситник: Не вижу причин пользоваться grunt. Вот как раз я лично сейчас больше всего люблю gulp, но мне не хватает двух функций из вебпака: сборка JS и CSS вместе по зависимостям, чтобы не указывать руками список файлов, а смотреть по require() в JS, и тут же хватать все стили, что лежат рядом с подключёнными JS-файлами.

Code Hipsters: А browserify?

Андрей Ситник: Ага, browserify + CSS-файлы, даже думаем с Шебановым написать свой плагин для gulp для этого, но задача сложная. Тем более хотелось бы сначала всё компилировать очень крутыми плагинами gulp, а потом собирать.

Code Hipsters: А PostCSS не может сконкатенировать все в один, его подключили — и вперед?

Андрей Ситник: Может, но всё в один не подходит для большинства проектов. Например есть отдельно стили для лэндинга, отдельно для главного сайта и отдельно для админки. У каждой этой точки входа есть своё дерево зависимостей.

Code Hipsters: Так это разные подпроекты, которые, по идее, отдельно собираются?

Андрей Ситник: У них часто есть общая кодовая база, кнопки, например, или попапы.

Code Hipsters: Так получается какой-то common-styles, который подключается во все «подпроекты».

Андрей Ситник: Ну вот не всегда во всех. Например, админка и главные стили делят попап между собой, а промо и главные стили — кнопки. Webpack уже решает эту проблему, он просто смотрит зависимости по require(). И Sprockets в рельсах так же — ты указываешь ему точку входа, он смотрит особые комментарии и подключает их в Sprockets. В Webpack ты указываешь зависимости CSS в JS

require('./style.css')
require('components/buttons.css')

Webpack тут лучше Sprockets, так как в Sprockets дерево зависимостей надо было дублировать в CSS и в JS, а в Webpack ты подключаешь «компонент» кнопки, а он уже сам подключает свои стили. Вот такую штуку хочется иметь в gulp, и чтобы ещё не переключаться между файлами использовать file-container.

Code Hipsters: А чем это будет отличаться от компонента?

Андрей Ситник: Web Components — это API изоляции в браузере, но да, идея общая. Скажем так — это компоненты для сборки.

Code Hipsters: Не, я о Web Components. С их сборкой vulcanize вроде помогает?

Андрей Ситник: Тоже другой подход, но у него минус в том, что он привязан к компонентам, и его для React сложнее реализовать — или для сайта без рендера на клиенте. Webpack сейчас делает этот компонентый подход для любого фреймворка.

Code Hipsters: Здорово!

Есть ли какая-то фронтенд-технология, которая тебя занимает больше остальных, если не говорить про PostCSS, конечно? Как тебе кажется, что из всего зоопарка будет мейнстримом через какое-то время?

Андрей Ситник: Меня интересует три вещи:

Компонентный подход явно станет мейнстримом, его давно уже внедрили в БЭМ-тулзах Яндексы. Webpack идёт туда, Web Components тоже. То есть главная идея будет в том, что мы будем разбивать интерфейс на маленькие части и хранить CSS, JS, шаблоны для этих частей вместе с одной папке.

Code Hipsters: А ты пользуешься тулами яндекса?

Андрей Ситник: Неа, они ад, но идеи там очень здравые.

Code Hipsters: У нас чуваки оттуда делали два доклада на митапе полгода назад, всех запутали и напугали.

Андрей Ситник: Ну это как раз пример того, как реализация убивает идею.

Code Hipsters: Мне кажется, их как раз Web Components съедят — не говоря о реализации.

Андрей Ситник: Web Components ещё долго ждать, Polymer не даёт никакой изоляции, так что изоляция — всё снова на БЭМ. Я боюсь, как бы инлайновые стили не выстрелили, тогда придётся заново изобретать CSS.

Code Hipsters: Так какие-то тулы этим уже занимаются, ты говорил.

Андрей Ситник: Ага, пока у них всё плохо.

Code Hipsters: И в любом случае руками никто инлайнить не будет, так что не много меняется.

Андрей Ситник: Да, тут потребуется новое поколение инструментов, и всё это — только под клиентский рендеринг. Не понятно со скоростью рендера. Думаю не выстрелит, даже если окажется быстрее чем по селекторам.

Code Hipsters: Спасибо огроменное за — ничего себе! — больше чем часовой разговор. Меня, например, в том, что за PostCSS будущее, ты полностью убедил. Было очень интересно!