Знакомство с gulp
Откладывайте Grunt в сторону, у нас появился новый исполнитель задач. Gulp - это интуитивная, код-над-конфигурацией, потоковая система сборки. Это факт.
Почему мне должно быть интересно? Хороший вопрос. Gulp’s код-над-конфигурацией делает не только лёгким написание задач, но его также легче читать и поддерживать.
Gulp использует node.js потоки, что делает быстрее сборку, так как не нужно писать на диск временные папки и файлы. Если вы хотите узнать подробнее о потоках - можете почитать —эту статью. Gulp позволяет вам вводить ваши файл(ы) источники, прокачивать их через множество плагинов и получать вывод в конце, вместо конфигурирования каждого плагина с входом и выходом как в Grunt. Давайте взглянем на характерный пример построения базового Sass в Grunt и в gulp:
Grunt:
sass: {
dist: {
options: {
style: 'expanded'
},
files: {
'dist/assets/css/main.css': 'src/styles/main.scss',
}
}
},
autoprefixer: {
dist: {
options: {
browsers: [
'last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'
]
},
src: 'dist/assets/css/main.css',
dest: 'dist/assets/css/main.css'
}
},
grunt.registerTask('styles', ['sass', 'autoprefixer']);
Grunt требует, чтобы каждый плагин конфигурировался отдельно, определяя источник и путь назначения для каждого плагина. Например, мы вводим один файл в Sass плагин, который далее сохраняет вывод. Далее нам нужно сконфигурировать Autoprefixer для ввода Sass вывода, который дальше выводит другой файл. Давайте взглянем на ту же конфигурацию с gulp:
Gulp:
gulp.task('sass', function() {
return gulp.src('src/styles/main.scss')
.pipe(sass({ style: 'compressed' }))
.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
.pipe(gulp.dest('dist/assets/css'))
});
С gulp мы используем для ввода только один файл. Он модифицируется Sass плагином, передаётся в Autoprefixer плагин и модифицируется и дальше на выходе мы получаем наш файл. Этот ускоряет процесс сборки, так как мы не читаем и не пишем ненужные файлы в конце и начале.
Итак, вы заинтересовались, дальше что? Давайте установим gulp и создадим базовый gulpfile с некоторыми ключевыми задачами для начала.
Установка gulp
Перед тем, как мы начнём конфигурировать задачи, нужно установить gulp:
npm install gulp -g
Это установит gulp глобально, давая доступ к gulp CLI. Далее нам нужно установить его локально для проекта. Зайдите - cd в вашу папку проекта и запустите следующее (убедитесь, что у вас есть файл пакетов package.json):
npm install gulp --save-dev
Это установит gulp локально для проекта и сохранит его в devDependencies в файле package.json.
Установка gulp плагинов
Мы собираемся установить некоторые плагины для следующих задач:
- Sass компиляция (gulp-ruby-sass)
- Автопрефиксы (gulp-autoprefixer)
- Минификация CSS (gulp-minify-css)
- JSHint (gulp-jshint)
- Обрезание (gulp-concat)
- Uglify (gulp-uglify)
- Сжатие изображений (gulp-imagemin)
- LiveReload (gulp-livereload)
- Кеширование изображений, поэтому только изменённые изображения будут сжиматься (gulp-cache)
- Оповещение об изменениях (gulp-notify)
- Очистка файлов для чистой сборки (del)
Для установки этих плагинов запустите следующую команду:
$ npm install gulp-ruby-sass gulp-autoprefixer gulp-minify-css gulp-jshint gulp-concat gulp-uglify gulp-imagemin gulp-notify gulp-rename gulp-livereload gulp-cache del --save-dev
Это установит все необходимые плагины и сохранит их в devDependencies в файл package.json. Полный список gulp плагинов можно найти здесь.
Загрузка плагинов
Далее, нам нужно создать gulpfile.js и загрузить плагины:
var gulp = require('gulp'),
sass = require('gulp-ruby-sass'),
autoprefixer = require('gulp-autoprefixer'),
minifycss = require('gulp-minify-css'),
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'),
livereload = require('gulp-livereload'),
del = require('del');
Хуух! Кажется намного больше работы, чем с Grunt, верно? Gulp плагины немного отличаются от Grunt плагинов – они предназначены для одной функции и делают её хорошо. Например: Grunt imagemin использует кеширование, чтобы избежать повторного сжатия изображений, которые уже сжали. С gulp это нужно делать с плагином кеширования, который также может быть использовать для других вещей. Это добавляет дополнительный слой возможности при процессе сборки. Круто правда?
Мы можем автозагрузить все установленные плагины как в Grunt, но в целях этого поста, мы будем следовать ручному методу.
Создание задач
Компиляция Sass, автопрефикс и минификация
Во-первых, мы сконфигурируем Sass компиляцию. Мы собираемся скомпилировать Sass в стиле expanded, пропустить его через автопрефиксер и сохранить в нужном месте. Далее создадим минифицированную.min версию, автообновим страницу и сообщим, что задача выполенна:
gulp.task('styles', function() {
return gulp.src('src/styles/main.scss')
.pipe(sass({ style: 'expanded' }))
.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
.pipe(gulp.dest('dist/assets/css'))
.pipe(rename({suffix: '.min'}))
.pipe(minifycss())
.pipe(gulp.dest('dist/assets/css'))
.pipe(notify({ message: 'Styles task complete' }));
});
Маленькое объяснение перед тем, как продолжим
gulp.task('styles', function() { ... )};
Мы определяем исходные файл(ы) черезgulp.src API. Также может быть использован глоб паттерн, например /**/*.scss для захвата множества файлов. Возврат потока делает его асинхронным, убеждаясь в том, что задача полностью завершилась перед тем, как мы получим сообщение о том, что она завершилась.
return gulp.src('src/styles/main.scss')
Мы используем .pipe() для прокачки исходных файлов через плагин. Обычно параметры для плагина находятся на их соответствующей GitHub странице. Я указал их выше для справки.
.pipe(gulp.dest('dist/assets/css'));
gulp.dest API - это где мы установим путь назначения. Задача может иметь различные пункты назначения, один для вывода расширенной версии и другой для минифицированной. Это продемонстрированно в задаче styles.
Я бы рекомендовал пройтись по gulp API документации, чтобы получить большее понимание этих методов. Это не так страшно, как кажется!
JSHint, обрезание и минификация JavaScript
Надеюсь, что у вас теперь есть хорошее представление как создавать задачи для gulp. Далее мы установим скриптовые задачи для проверки, обрезания и минификации:
gulp.task('scripts', function() {
return gulp.src('src/scripts/**/*.js')
.pipe(jshint('.jshintrc'))
.pipe(jshint.reporter('default'))
.pipe(concat('main.js'))
.pipe(gulp.dest('dist/assets/js'))
.pipe(rename({suffix: '.min'}))
.pipe(uglify())
.pipe(gulp.dest('dist/assets/js'))
.pipe(notify({ message: 'Scripts task complete' }));
});
Запомните одну вещь - нам нужно определить репортёр для JSHint. Я использую репортёр по-умолчанию, который подойдёт для большинства. Более подробно об этом можно узнать на JSHint вебсайте.
Сжатие изображений
Далее мы установим сжатие изображений:
gulp.task('images', function() {
return gulp.src('src/images/**/*')
.pipe(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true }))
.pipe(gulp.dest('dist/assets/img'))
.pipe(notify({ message: 'Images task complete' }));
});
Это будет брать любые исходные изображения и прогонять их через плагин imagemin. Мы можем пойти чуть далее и использовать кеширование для избежания пересжатия уже сжатых изображений каждый раз при запусске задач. Всё, что нам нужно - это плагин gulp-cahce, который мы уже установили. Для настройки, нам нужно поменять эту строку:
.pipe(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true }))
На эту:
.pipe(cache(imagemin({ optimizationLevel: 5, progressive: true, interlaced: true })))
Теперь будут сжиматься только новые изображения. Круто!
Очистка!
Перед деплоем хорошо будет очистить наши папки назначения и пересобрать файлы - просто на случай если что-то было удалено из исходных файлов и осталось в папках назначения:
gulp.task('clean', function(cb) {
del(['dist/assets/css', 'dist/assets/js', 'dist/assets/img'], cb)
});
Нам не нужно использовать здесь плагин gulp, мы можем использоваться преимуществом Node модулей прямо всередине gulp. Мы будем использовать обратный вызов (cb) для обеспечения окончания задачи перед выходом.
Задача по-умолчанию
Мы можем создать задачу по-умолчанию используя $ gulp для запуска всех трёх созданных задач:
gulp.task('default', ['clean'], function() {
gulp.start('styles', 'scripts', 'images');
});
Обратите внимание на дополнительный массив в gulp.task. Здесь мы можем определить зависимости для заданий. В этом примере, чистая задача будет запущена перед задачами в gulp.start. Задачи в Gulp выполняются одновременно и не имеют порядка, в котором они заканчиваются, поэтому нам нужно убедится, что задание clean завершено перед запуском дополнительных задач.
Внимание: Есть много советов проти использования gulp.start перед выполнением задач в массиве зависимостей, но в этом сценарие clean позволяет избежать негативные моменты и является лучшим выходом.
Наблюдение
Для наблюдения за нашими файлами и выполнения необходимой задачи при их смене, нам во-первых нужно создать новую задачу и далее использовать gulp.watch API для начала наблюдения за файлами:
gulp.task('watch', function() {
// Наблюдение за .scss файлами
gulp.watch('src/styles/**/*.scss', ['styles']);
// Наблюдение .js файлами
gulp.watch('src/scripts/**/*.js', ['scripts']);
// Наблюдение файлами изображений
gulp.watch('src/images/**/*', ['images']);
});
Мы укажим файлы, за которыми нужно наблюдать, через gulp.watch API и определим какие задачи нужно запускать через массив зависимостей. Теперь мы можем запускать $ gulp watch и любые изменения в .scss, .js или файлах изображений запустят соответствующие задачи.
LiveReload
Gulp может также брать на себя автоматическое обновение страницы при изменении файла. Нам нужно несколько модифицировать нашу watch задачу и сконфигурировать сервер LiveReload.
gulp.task('watch', function() {
// Создание LiveReload сервера
livereload.listen();
// Наблюдайте за всеми файлами в dist/, перезагружайтесь при изменениях
gulp.watch(['dist/**']).on('change', livereload.changed);
});
Чтобы заставить это работать, вам нужно установить и сделать рабочим плагин браузера LiveReload. Вы также можете разместить этот кусок вручную.
Собираем всё вместе
Теперь у нас есть полный gulpfile, можна взять gist отсюда:
/*!
* gulp
* $ npm install gulp-ruby-sass gulp-autoprefixer gulp-minify-css gulp-jshint gulp-concat gulp-uglify gulp-imagemin gulp-notify gulp-rename gulp-livereload gulp-cache del --save-dev
*/
// Загрузка плагинов
var gulp = require('gulp'),
sass = require('gulp-ruby-sass'),
autoprefixer = require('gulp-autoprefixer'),
minifycss = require('gulp-minify-css'),
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'),
livereload = require('gulp-livereload'),
del = require('del');
// Стили
gulp.task('styles', function() {
return gulp.src('src/styles/main.scss')
.pipe(sass({ style: 'expanded', }))
.pipe(autoprefixer('last 2 version', 'safari 5', 'ie 8', 'ie 9', 'opera 12.1', 'ios 6', 'android 4'))
.pipe(gulp.dest('dist/styles'))
.pipe(rename({ suffix: '.min' }))
.pipe(minifycss())
.pipe(gulp.dest('dist/styles'))
.pipe(notify({ message: 'Styles task complete' }));
});
// Скрипты
gulp.task('scripts', function() {
return gulp.src('src/scripts/**/*.js')
.pipe(jshint('.jshintrc'))
.pipe(jshint.reporter('default'))
.pipe(concat('main.js'))
.pipe(gulp.dest('dist/scripts'))
.pipe(rename({ suffix: '.min' }))
.pipe(uglify())
.pipe(gulp.dest('dist/scripts'))
.pipe(notify({ message: 'Scripts task complete' }));
});
// Изображения
gulp.task('images', function() {
return gulp.src('src/images/**/*')
.pipe(cache(imagemin({ optimizationLevel: 3, progressive: true, interlaced: true })))
.pipe(gulp.dest('dist/images'))
.pipe(notify({ message: 'Images task complete' }));
});
// Очистка
gulp.task('clean', function(cb) {
del(['dist/assets/css', 'dist/assets/js', 'dist/assets/img'], cb)
});
// Задача по-умолчанию
gulp.task('default', ['clean'], function() {
gulp.start('styles', 'scripts', 'images');
});
// Наблюдение
gulp.task('watch', function() {
// Наблюдение за .scss файлами
gulp.watch('src/styles/**/*.scss', ['styles']);
// Наблюдение за .js файлами
gulp.watch('src/scripts/**/*.js', ['scripts']);
// Наблюдение за файлами изображений
gulp.watch('src/images/**/*', ['images']);
// Создание сервера LiveReload
livereload.listen();
// Watch any files in dist/, reload on change
gulp.watch(['dist/**']).on('change', livereload.changed);
});
Также я собрал для сравнения Gruntfile, который выполняет те же задачи, можете его посмотреть в этом же gist.
Исходная статья Getting started with gulp
11-12-2014 gulp.js Frontend Виктор Матушевский