Расставание с Sass: дело не в тебе, дело во мне

Перевод статьи Breaking up with Sass: it’s not you, it’s me с сайта benfrain.com, автор — Бен Фрейн.

В данный момент мой процесс работы с CSS налажен практически идеально. Я пишу на Sass, компилирую код с помощью Libsass и добавляю браузерные префиксы с помощью PostCSS/Autoprefixer через Gulp/Grunt. Так зачем же мне рушить всё это?

Меньше года назад мой интерес привлек пост Николаса Галлахера. В нем Николас описывал, как он/они (команда Twitter) использовали JS-инструмент для постобработки CSS.

Позже Дэвид Кларк поведал мне о PostCSS и его использовании вместо Sass. Впоследствии он писал на эту тему вот здесь: http://davidtheclark.com/excited-about-postcss/

Восторг Дэвида снова разжег мое любопытство, и с тех пор у меня начала зарождаться идея об использовании PostCSS для всех моих CSS-нужд (PostCSS – это та «основа», которая лежит «в основе» других инструментов, таких как Autoprefixer).

Давайте проясним. В Sass нет ничего плохого. Мои поводы для разрыва наших отношений преимущественно эгоистичны. Позвольте, я вам всё расскажу:

Все, что можно написать на JS, будет написано на JS

Это слова Джефа Этвуда, сказанные в 2007. И сегодня они находят у меня еще больший отклик; я не думаю, что JavaScript может исчезнуть в ближайшее время.

В моем случае все задачи, связанные с разработкой, сосредоточены на фронтенде. В данный момент мои навыки JavaScript находятся на уровне «Начинающий». Я бы хотел однажды в будущем начать считать себя «Продвинутым», но чтобы дойти до этого уровня, мне нужно чаще использовать JavaScript.

Я довольно хорошо знаю Sass (знаю, вам хочется на это надеяться, учитывая, что я написал книгу на эту тему). Однако, мое понимание и непрекращающееся изучение этого языка не применимо в других отраслях. Несмотря на то, что многие соглашения, с которыми мне довелось столкнуться, и которые я теперь понимаю, являются базовыми вещами из области информатики, с которыми я не познакомился бы при других обстоятельствах (поскольку у меня нет опыта в этой области), существующий синтаксис и его недостатки уникальны. Я не смогу применить эти знания в другом месте.

Кроме того, использование языка Sass делает меня зависимым от особенностей, которые добавляют или убирают авторы Sass. Во многих отношениях это хорошо. Однако, у меня нет навыков C или Ruby, чтобы расширить возможности языка в случае необходимости. К примеру, мне бы очень хотелось увидеть поддержку подстановок, но один из авторов настаивает на том, что это никогда не станет частью языка. Это расстраивает меня. Моих способностей не достаточно, чтобы что-то изменить.

Что мне нужно от Sass

Я люблю, когда мой стиль написания CSS близок к CSS. Я имею в виду, что пользуюсь такими инструментами Sass, как циклы, функции и миксины, только если мне это действительно нужно. Для меня главные преимущества Sass всегда ассоциировались с переменными (за согласованность кода) и модулями (за его организацию). Однако, бывали случаи, когда и более новые возможности, такие как структуры данных, доказывали, что могут быть действительно полезны.

Я составил список из вещей, которые необходимы мне в таблице стилей (в скобках указаны инструменты, которые обеспечивают эти потребности на сегодняшний день):

  • переменные [Sass]
  • структуры данных [Sass]
  • циклы (в структурах данных) [Sass]
  • вложенность [Sass]
  • преобразования цвета (особенно HSB) [Sass]
  • автоматическая расстановка браузерных префиксов [PostCSS/Autoprefixer через Gulp]
  • возможность группировать схожие медиазапросы (а почему бы и нет) [Gulp]
  • повторная минификация CSS (после того, как медиазапросы и Autoprefixer сделают свое дело) [Gulp]

На данный момент все больше и больше моих потребностей обеспечиваются с помощью систем постобработки и сборки; еще пару лет назад их даже не существовало. В те времена расстановкой браузерных префиксов у меня занимался Compass. Минификацией у меня занимался Sass. Но теперь все изменилось.

Учитывая мои нынешние потребности, мне захотелось узнать, можно ли (и стоит ли) перейти от существующих инструментов, завязанных на Sass, в мир PostCSS.

Я подробно задокументирую этот процесс, прежде чем попытаюсь собрать воедино все мои размышления по этому поводу.

Переход от Sass к PostCSS

В данный момент моим инструментом для сборки является Gulp. Я использовал его как «пропуск» к PostCSS.

Со временем мне захотелось стать «ближе к корням» и использовать Node непосредственно в качестве инструмента для сборки, хотя я еще не дошел до этого. Для справки советую ознакомиться со статьями Кита Сёркила "Почему мы должны прекратить использовать Grunt" и "Использование nmp в качестве инструмента для сборки" на эту тему.

Перед вами самый простой gulpfile.js, который я использовал для тестирования PostCSS. На самом деле с PostCSS здесь работает лишь небольшая подгруппа команд, но я привожу весь список ради полноты.

var gulp = require('gulp'),
    jshint = require('gulp-jshint'),
    uglify = require('gulp-uglify'),
    imagemin = require('gulp-imagemin'),
    rename = require('gulp-rename'),
    concat = require('gulp-concat'),
    notify = require('gulp-notify'),
    cache = require('gulp-cache'),
    browserSync = require('browser-sync'),
    reload      = browserSync.reload;
 
var gutil = require('gulp-util');
var postcss = require('gulp-postcss');
var simplevars = require('postcss-simple-vars');
var autoprefixer = require('autoprefixer-core');
var mqpacker = require('css-mqpacker');
var csswring = require('csswring');
var nestedcss = require('postcss-nested');
var corepostcss = require('postcss');
var categories = require('./data/cat-colors.json');
 
var dataloop = function(css) {
    for ( var category in categories.colorList ) {
        var colorSet = categories.colorList[category];
        var borderTop = colorSet[0];
        var borderBottom = colorSet[1];
        var rule = corepostcss.rule({ selector: '.cat-' + category });
        rule.append({ prop: 'border-top', value: '1px solid ' + borderTop});
        rule.append({ prop: 'border-bottom', value: '1px solid ' + borderBottom + ";"});
        css.append(rule);
    }
};
 
gulp.task('css', function () {
    var processors = [
        autoprefixer({browsers: ['last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4']}),
        simplevars,
        nestedcss,
        dataloop
    ];
    return gulp.src('./preCSS/*.css')
        .pipe(postcss(processors))
        .pipe(gulp.dest('./dest'));
});
 
// Статический сервер
gulp.task('browser-sync', function() {
     browserSync({
          server: {
                baseDir: "./"
          }
     });
});
 
// Конкатенация и минификация JS
gulp.task('scripts', function() {
    return gulp.src('js/*.js')
        .pipe(concat('all.js'))
        .pipe(gulp.dest('dist'))
        .pipe(rename('all.min.js'))
        .pipe(uglify())
        .pipe(gulp.dest('dist'))
        .pipe(reload({stream:true}));
});
 
// Изображения
gulp.task('images', function() {
  return gulp.src('img/**/*')
    .pipe(cache(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true })))
    .pipe(gulp.dest('dist/images'))
    .pipe(notify({ message: 'Images task complete' }));
});
 
// Отслеживание
gulp.task('watch', function() {
 
    // Отслеживание файлов .css
    gulp.watch('preCSS/**/*.css', ['css', browserSync.reload]);
 
    // Отслеживание файлов .js
    gulp.watch(['js/**/*.js','main.js'], ['scripts', browserSync.reload]);
 
    // Отслеживание файлов изображений
    gulp.watch('img/**/*', ['images']);
 
    // Отслеживание всех файлов в папке dist/, перезагрузка при изменении
    gulp.watch("*.html", browserSync.reload);
 
});
 
gulp.task('default', ['css', 'browser-sync', 'scripts', 'watch']);

Удобные плагины, подобные Sass

Мне хотелось, в частности, протестировать вложенность, переменные и циклическую обработку в структурах данных в стиле PostCSS. Изначально функционал PostCSS довольно скромен. Все необходимые дополнительные возможности вы добавляете с помощью множества разнообразных плагинов.

Соответственно, Плагины PostCSS предоставляют методы для работы с переменными, вложенностью и миксинами, подобные тем, к каким я привык, работая с Sass.

Эти плагины просто работают. Мне не пришлось ничего настраивать, поэтому я не буду расписывать их в подробностях.

Например, благодаря плагину PostCSS Simple Variables вы можете использовать переменные, как если бы работали с Sass: $variable-name: value;. Если же вам нужно вставить переменную, синтаксис слегка отличается, $(variable-name)_normal-thing (в отличие от #{$variable-name} в Sass).

Возможно, вам будет интересно узнать, что если вы желаете поближе познакомиться с действующей Спецификацией W3C для авторских свойств, вы можете использовать плагин https://github.com/postcss/postcss-custom-properties.

Если вам нужны миксины, для вас существует PostCSS Mixins. Синтаксис, опять же, намеренно сделан похожим на Sass. На самом деле, я их пока нигде не использовал. Мне было интереснее реализовать структуры данных, потому что для них пока не существует ни одного готового инструмента.

Структуры данных и использование циклов

Мне не так часто необходимо циклически обрабатывать структуры данных, но когда такая задача стоит, меня, безусловно, радует наличие подобной возможности в Sass. В текущих проектах, над которыми я работаю, есть парочка экземпляров, с которыми я буквально не смог бы справиться без этой возможности. Это была одна из первых вещей, о которой я начал расспрашивать. И последующие расспросы и огромное количество помощи, что предоставил Том (серьезно, без Тома я бы ничего не достиг в JS), открыли мне способ добиться этого в PostCSS.

В отличие от Sass, который намеренно смешивает логику со стилями, PostCSS в идеале попросил бы вас держать логику и программирование подальше от таблиц стилей. Здесь меня разрывает на части. Я ценю оба подхода. Но давайте отложим философские споры в сторону и просто посмотрим, как можно загружать структуры данных и обрабатывать их циклически в мире PostCSS.

Создание ссылки на структуру данных в PostCSS

Замечательным фактом в использовании PostCSS является то, что это — JavaScript. Поэтому вам не нужно прикладывать дополнительных усилий, чтобы начать использовать структуры данных типа JSON. В представленном выше Gulpfile я просто ставлю ссылку на мою тестовую JSON-структуру данных следующим образом:

var categories = require('./data/cat-colors.json');

Перед вами содержимое этого JSON-файла:

{
    "colorList": {
        "c1"  : ["#000000", "#000011"],
        "c2"  : ["#000011", "#000022"],
        "c3"  : ["#000022", "#000033"],
        "c4"  : ["#000033", "#000044"],
        "c7"  : ["#000044", "#000055"],
        "c8"  : ["#000055", "#000066"],
        "c9"  : ["#000066", "#000077"],
        "c10" : ["#000077", "#000088"]
    }
}

Я хочу циклически пройти по этим данным и выбрать первое 16-ричное значение для цвета border-top, а второе 16-ричное значение – для цвета border-bottom. Кроме того, я хочу использовать значение ключа для создания селектора. Вот цикл из представленного выше файла gulpfile.

var dataloop = function(css) {
    for ( var category in categories.colorList ) {
        var colorSet = categories.colorList[category];
        var borderTop = colorSet[0];
        var borderBottom = colorSet[1];
        var rule = corepostcss.rule({ selector: '.cat-' + category });
        rule.append({ prop: 'border-top', value: '1px solid ' + borderTop});
        rule.append({ prop: 'border-bottom', value: '1px solid ' + borderBottom + ";"});
        css.append(rule);
    }
};

Если вы уже привыкли писать на JavaScript, вам не нужно объяснять вышеупомянутый цикл. Если же вы пока еще не очень много используете JavaScript, позвольте разжевать вам то, что здесь происходит.

Сначала мы создаем переменную, которая будет служить ссылкой на функцию:

var dataloop = function(css) {

Ее аргумент (css) это то, что будет передано в нее после завершения работы PostCSS (CSS).

Далее следует цикл:

for ( var category in categories.colorList ) {

Не забывайте, что мы уже создали ссылку на наши данные JSON в переменной categories. Внутри этого JSON мы хотим найти объект colorList, поэтому с помощью записи через точку мы получаем categories.colorList. Переменная var category просто присваивает динамическое имя, чтобы использовать его в качестве итерации цикла. После этого мы хотим создать ссылку на каждую «строку» этого JSON. Для этого мы используем следующую переменную:

var colorSet = categories.colorList[category];

Создав ссылку, мы можем получить любое из значений внутри массива (например, colorSet[0]), чтобы присвоить их тем правилам CSS, которые нам необходимы. Остальная часть цикла просто создает правила и добавляет их в CSS.

var rule = corepostcss.rule({ selector: '.cat-' + category });
rule.append({ prop: 'border-top', value: '1px solid ' + borderTop});
rule.append({ prop: 'border-bottom', value: '1px solid ' + borderBottom + ";"});
css.append(rule);

Вы могли заметить, что я дважды создал ссылку на postCSS в начале моего файла gulpfile. Это связано с тем, что я столкнулся с некоторыми трудностями, когда пытался заставить это работать с помощью var postcss = require('gulp-postcss');. Поэтому, в конце концов я решил использовать «нормальный» PostCSS, чтобы получить доступ к методу rule. Если кто-то знает, что я здесь делаю не так – пожалуйста, сообщите мне.

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

.cat-c1 {
    border-top: 1px solid #000000;
    border-bottom: 1px solid #000011;
}

Используемое задание для PostCSS

Задание для использования PostCSS в gulp выглядит очень симпатично и просто:

gulp.task('css', function () {
    var processors = [
        autoprefixer({browsers: ['last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4']}),
        simplevars,
        nestedcss,
        dataloop
    ];
    return gulp.src('./preCSS/*.css')
        .pipe(postcss(processors))
        .pipe(gulp.dest('./dest'));
});

Как видите, в переменной processors мы просто перечисляем все плагины, которые хотим использовать для обработки CSS. Обратите внимание, что в списке представлена и наша функция dataloop. Ссылки на любые дополнительные функции/плагины, которые вам необходимы, могут быть добавлены таким же образом. Если они находятся не в файле gulpfile, вы просто добавляете их с помощью команды require.

Соответственно, возможности делать различные «вещи» с CSS, по сути, безграничны (либо ограничены лишь вашим воображением и способностями).

Итак, в заключение этого раздела, думаю, можно смело сказать, что почти все вопросы, для решения которых вам был необходим Sass, можно решить с помощью PostCSS.

Почему вам не стоит использовать PostCSS?

Как и в случае со всеми популярными фреймворками и инструментами, существует несколько веских причин для их использования. Они упрощают задачу по привлечению новых разработчиков в ваши проекты. Если вы можете сказать «Мы используем Angular», или «Мы используем Ember», или «Мы используем Sass» и т.д., новым разработчикам проще уловить суть вашей работы. Всё это функциональные, эффективные и хорошо задокументированные проекты, несмотря на любые недостатки.

Двигаясь в этом направлении, вы в некотором роде создаете индивидуальный инструмент для обработки CSS. Это палка о двух концах.

Кроме того, в возможности включать логику в таблицы стилей есть определенные плюсы. Директива @warn может быть довольно полезной. Или оборачивание разделов в выражения @if, чтобы они выводились только при определенных обстоятельствах. Эти возможности для меня очень полезны. И если вы используете Sass, то они у вас есть – надежные, готовые, ждущие вашей команды.

Почему стоит использовать PostCSS?

PostCSS предоставляет буквально безграничные средства для манипуляции CSS любым необходимым вам образом.

Он невероятно быстр. Мне еще предстоит выполнить тесты и подтвердить это доказательствами в ходе работы с огромными проектами, но я рискну предположить, что он быстрее Libsass.

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

Заключение

Большинство людей, пишущих таблицы стилей в данный момент (с радостью) используют Sass, и прямо сейчас они не получат значительных преимуществ, бросив всё это ради PostCSS. Я надеюсь, что в этом посте смог объяснить, что на этот шаг можно пойти в философских и/или личных целях. Моим стандартным советом ко всем, кто ищет инструмент, который поможет ему организовать и поддерживать CSS, будет «используйте Sass».

При этом некоторые аргументы утверждают, что выбор PostCSS предпочтительнее существующих препроцессоров, поскольку он позволяет вам использовать действующий синтаксис W3C для таких вещей, как переменные/авторские свойства. Проект cssnext доводит этот подход до логического завершения, добавляя возможность уже сегодня использовать целый ряд синтаксических модулей CSS, которые пока находятся на стадии рассмотрения или в разработке. Есть мнение, что тем самым они позволяют писать таблицы стилей с заделом на будущее. И хотя некоторым эта идея может показаться привлекательной, меня такой подход не убедил.

Спецификация W3C, не утвержденная или не внедренная более чем парой производителей, не стабильнее и не перспективнее, чем любая произвольная абстракция. Если вы считаете иначе, то осмелюсь спросить, пробовали ли вы хоть раз писать документацию к таким вещам, как Flexbox или linear-gradients в последние годы? Кроме того, для преобразования переменной из одного формата в другой, скорее всего, понадобится чуть больше, чем потратить 10 минут в любом надежном текстовом редакторе. 

Что касается скорости, и Libsass, и PostCSS работают невероятно быстро. PostCSS имеет некоторое преимущество, но я не уверен, что вы сможете заметить или ощутить какую-либо явную выгоду. Возможно, на очень больших проектах – да, но для большинства пользователей, скорее всего, нет. Со временем я намерен лично провести соответствующие тесты.

Также вы можете упростить импорт для подстановки любого из инструментов с помощью инструмента для сборки, такого как Gulp (существующий плагин для импорта со встраиванием для PostCSS не поддерживает подстановку), поэтому в этом отношении у PostCSS тоже нет преимуществ.

Зато, на мой взгляд, настоящая прелесть PostCSS заключается в освобождении от ограничений и в широкой области применения этих навыков. Решение ваших задач, связанных с CSS, с помощью инструментов, управлять которыми приходится с помощью JavaScript, либо дает вам свободу, если вы уже хорошо владеете языком, либо будет полезным времяпрепровождением, если вы хотите наслаждаться работой на веб-фронтенде (а может быть, и на бэкенде) в будущем.

P.S. Это тоже может быть интересно:

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

Получать новые комментарии по электронной почте. Вы можете подписаться без комментирования.