habrahabr

CSS-препроцессоры против постпроцессоров

  • понедельник, 8 сентября 2014 г. в 03:10:51
http://habrahabr.ru/post/235929/

CSS-препроцессоры прекрасны, они сделали революцию в мире интернет-вёрстки. Создание кросс-браузерного CSS стало гораздо проще, потому что все вендорные префиксы и хаки можно завернуть в примеси, заглушки или что у вас там. До сих пор этого вполне хватало, но так как мы неистово одержимы контролем, хотим большего. Всегда. БОООООЛЬШЕ. Скажи привет CSS-постпроцессорам!

Postprocessing? я запутался!



Это неудивительно, мне кажется “postprocessing” не слишком подходящим словом, но он вполне способно донести саму суть – CSS-препроцессоры (Sass, Less и т.д.) читают и собирают язык расширения в CSS, тогда как постпроцессоры читают и собирают сам CSS. Это ужасно, простите, но если было непонятно, то теперь всё должно встать на свои места.

Уже существует несколько постпроцессоров, например – Autoprefixer, PostCSS и rework.

Зачем использовать постпроцессор?



Что ж, давайте возьмём для примера Autoprefixer, он читает ваш CSS и добавляет вендорные префиксы, используя при этом базу данных Can I Use. Представьте, что вы пишете на Sass и хотите применить правило из CSS3, плохо поддерживаемое без префиксов, так что используете примесь:

@mixin box-sizing($value) {
  -webkit-box-sizing: $value;
     -moz-box-sizing: $value;
          box-sizing: $value;
}
.box {
  @include box-sizing(border-box);
}


Почему Autoprefixer лучше? Потому что можно вот так:

.box {
  box-sizing: border-box;
}


Пишите на Sass/Less/Stylus/CSS без забот, потому что готовый CSS будет прочтён и в него будут добавлены нужные вендорные префиксы:

.box {
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
}


Есть хотя бы 3 преимущества этого подхода:

  1. Не нужно думать о том, какие правила нужно повторять с префиксами (или нужно ли вот это правило использовать как примесь)
  2. Потом можно выбрать браузеры, которые вы будете поддерживать, просто настроив Autoprefixer. Подробности ниже.
  3. Быстрее, чем Sass. (сомнительное высказывание – примечание переводчика)


У Autoprefixer отличная поддержка любых инструментов сборки, которые вы можете использовать. Для дальнейших примеров я буду использовать Gulp (простите, кто использует что-то другое), так вот ваша задача может выглядеть так:

var sass = require('gulp-ruby-sass');
var autoprefixer = require('gulp-autoprefixer');

gulp.task('styles', function() {
  return gulp.src('app/styles/main.scss')
    .pipe(sass())
    .pipe(autoprefixer({browsers: ['last 3 versions']}))
    .pipe(gulp.dest('dist/styles'));
});


Я попросил Autoprefixer добавить вендорные префиксы для 3 последних выпусков любого браузера.

Новый уровень



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

Представьте, что вы хотите использовать прозрачность, как белый человек:

.box {
  opacity: 0.5;
}


Но IE8 говорит “Что это за прозрачность такая? Это что-то, что ты ешь?”, так что вам нужно добавить хак. Вы действительно собираетесь использовать примесь просто потому, что нужно добавить один хак для одного правила, который впоследствии будет лишним? Вместо этого можно написать простой обработчик PostCSS:

function(css) {
  css.eachDecl(function(decl, i) {
    if (decl.prop === 'opacity') {
      decl.parent.insertAfter(i, {
        prop: '-ms-filter',
        value: '"progid:DXImageTransform.Microsoft.Alpha(Opacity=' + (parseFloat(decl.value) * 100) + ')"'
      });
    }
  });
}


Он сделает цикл на каждом объявлении, и, если он встретит opacity, добавит для IE8 хак. Теперь нужно подключить его к нашей задаче Gulp:

var sass = require('gulp-ruby-sass');
var autoprefixer = require('gulp-autoprefixer');
var postcss = require('gulp-postcss');
var opacity = function(css) {
  css.eachDecl(function(decl, i) {
    if (decl.prop === 'opacity') {
      decl.parent.insertAfter(i, {
        prop: '-ms-filter',
        value: '"progid:DXImageTransform.Microsoft.Alpha(Opacity=' + (parseFloat(decl.value) * 100) + ')"'
      });
    }
  });
};

gulp.task('styles', function() {
  return gulp.src('app/styles/main.scss')
    .pipe(sass())
    .pipe(autoprefixer({browsers: ['last 3 versions']}))
    .pipe(postcss([opacity]))
    .pipe(gulp.dest('dist/styles'));
});


Если вы хотите быть крутым, можете добавить Autoprefixer как обработчик PostCSS вместо расширения Gulp, так что вместо gulp-autoprefixer придётся требовать autoprefixer-core:

var sass = require('gulp-ruby-sass');
var autoprefixer = require('autoprefixer-core');
var postcss = require('gulp-postcss');
var opacity = function(css) {
  css.eachDecl(function(decl, i) {
    if (decl.prop === 'opacity') {
      decl.parent.insertAfter(i, {
        prop: '-ms-filter',
        value: '"progid:DXImageTransform.Microsoft.Alpha(Opacity=' + (parseFloat(decl.value) * 100) + ')"'
      });
    }
  });
};

gulp.task('styles', function() {
  return gulp.src('app/styles/main.scss')
    .pipe(sass())
    .pipe(postcss([
      autoprefixer({browsers: ['last 3 versions']}),
      opacity
    ]))
    .pipe(gulp.dest('dist/styles'));
});


Теперь после каждого выполнения задачи у нас будет такое правило:

.box {
  opacity: 0.5;
  -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
}


Кошмар… но это именно то, чего мы хотели. Теперь, если мы захотим прекратить париться об IE8, сможем просто отключить обработчик из нашей задачи Gulp.

Так как же выбрать между пре- и пост-процессорами?



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

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

Меня это действительно волнует, а вас?
А вы уже используете постпроцессоры?

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

Проголосовало 269 человек. Воздержалось 59 человек.