Gulp — как глоток свежего воздуха после Grunt
На днях состоялся релиз таск-менеджера gulp.js (англ. «глоток» [/gʌlp/, /галп/]), который является достойной альтернативой сверхпопулярному приложению Grunt. Давайте попробуем разобраться, зачем был создан gulp, и в чем его отличие от Grunt.
Когда дело доходит до JavaScript таск-менеджеров, Grunt — неоспоримый король среди них. Ну, по крайней мере, еще совсем недавно дела обстояли именно так. Ранее, в этом году, часть команды Fractal выразила свою озабоченность положением дел в проекте Grunt. Они выступили с предложением взять все хорошие идеи и сильные стороны Grunt, переработать, и воплотить их в новом проекте. Проект был назван лаконично — gulp. По сути, сейчас он решает те же проблемы, что и Grunt, однако, между ними есть очень большие различия «под капотом». Давайте рассмотрим эти различия подробнее.
Что такое «таск-менеджер»?
Кто-то из вас возможно уже знаком с Grunt. Для тех, кто не в курсе, немного проясним, что же такое таск-менеджер под JavaScript.
Таск-менеджер — небольшое приложение, которое используется для автоматизации скучных и рутинных, но, от того, не менее важных, задач, которые приходится постоянно выполнять в процессе разработки проекта. Такие задачи включают в себя, к примеру, запуск модульных тестов, конкатенацию файлов, минификацию, препроцессинг CSS. Просто создав таск-файл, вы можете проинструктировать таск-менеджер, каким образом следует выполнить ту или иную задачу. И после этого вы можете заниматься своими делами. Довольно простая идея, которая позволяет сэкономить очень много времени, и помогает сохранять фокус на задачах, связанных непосредственно с разработкой проекта.
Различия
Теперь, когда вы понимаете, о чем идет речь, вы можете задать резонный вопрос: «Чем, собственно, gulp отличается от Grunt, и почему меня должны волновать эти различия?»
Потоковая передача данных
Gulp, как система, построена вокруг концепции потоковой передачи данных. Здесь я хотел бы углубиться объяснение этой самой потоковой передачи, но, к счастью, уже есть замечательный источник, из которого вы можете почерпнуть знания о ней. Если вам, конечно, это интересно.
Если не вдаваться в подробности, потоковая передача дает вам больше контроля над происходящим и избавляет вас от промежуточных папок и файлов. Вы передаете файл в gulp, а затем сохраняете результат в другой файл. Всё очень просто.
Плагины
Когда дело доходит до расширения функциональности, ключевая мысль gulp в том, что каждый плагин должен выполнять только одно простое действие. Сам gulp всего лишь соединяет и организует эти действия в задачи. Здесь нет плагинов, дублирующих действия друг друга, и конфликтующих между собой или основным функционалом.
Пиши код, не занимайся настройками
Мне лично больше всего по душе то, что gulpfile — это прежде всего код, а не файл с настройками. Gulp следует спецификации CommonJS, и если вы знакомы с Node, у вас не возникнет никаких проблем. Gulpfile выглядит аккуратно и читаемо, благодаря тому, что код уже структурирован известным вам способом. Его написание не должно вызвать у вас существенных затруднений.
Примеры
Всё это трудно осмыслить без примеров кода, так что я продемонстрирую вам два таких примера. Ниже мы создадим gruntfile и gulpfile. Оба будут делать линтинг, конкатенацию и минификацию js-файлов в нашем проекте. Также, оба таск-менеджера должны отслеживать изменения в файлах проекта, и запускать связанные с этими файлами задачи повторно.
Начнем с gruntfile, а затем я покажу вам, как те же самые настройки выглядит в gulpfile. Это поможет вам наглядно понять, как работают оба таск-менеджера, и как именно gulp использовал и развил идеи Grunt.
gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
concat: {
options: {
separator: ';'
},
dist: {
src: ['src/**/*.js'],
dest: 'dist/<%= pkg.name %>.js'
}
},
uglify: {
dist: {
files: {
'dist/<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>']
}
}
},
jshint: {
files: ['gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
options: {
globals: {
jQuery: true,
console: true,
module: true,
document: true
}
}
},
watch: {
files: ['<%= jshint.files =>'],
tasks: ['jshint', 'concat', 'uglify']
}
});
// Подключаем наши плагины
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch');
// Регистрируем задачу по умолчанию
grunt.registerTask('default', ['jshint', 'concat', 'uglify']);
};
gulpfile.js
var gulp = require('gulp');
var jshint = require('gulp-jshint');
var concat = require('gulp-concat');
var rename = require('gulp-rename');
var uglify = require('gulp-uglify');
// Линтинг файлов
gulp.task('lint', function() {
gulp.src('./src/*.js')
.pipe(jshint())
.pipe(jshint.reporter('default'));
});
// Конкатенация и минификация файлов
gulp.task('minify', function(){
gulp.src('./src/*.js')
.pipe(concat('all.js'))
.pipe(gulp.dest('./dist'))
.pipe(rename('all.min.js'))
.pipe(uglify())
.pipe(gulp.dest('./dist'));
});
// Действия по умолчанию
gulp.task('default', function(){
gulp.run('lint', 'minify');
// Отслеживаем изменения в файлах
gulp.watch("./src/*.js", function(event){
gulp.run('lint', 'minify');
});
});
Используя gulp, мы уменьшили количество строк в нашем коде с 52 до 30. Вы должны были заметить, что хотя нам и потребовалось одинаковое количество плагинов, два из них различаются, несмотря на то, что мы выполняем совершенно одинаковую задачу в первом и во втором примерах. Это так же наглядно демонстрирует различие в подходах к плагинам, о котором я писал выше.
В gulp нет необходимости в плагине watch
, потому как возможность реагировать
на изменения в файлах уже включена в ядро. Это одна из тех функциональностей,
которую вы ожидаете увидеть по умолчанию, а не посредством расширения
возможностей с помощью плагина.
В дополнение, переименование файла в Grunt выполняется с помощью плагина
uglify
. Получается, что один и тот же плагин отвечает как за минификацию
кода, так и за переименование файла на выходе.
В gulp же каждый плагин выполняет одно простое действие, и отвечает только за
него. Для переименования файла мы просто подключим плагин gulp-rename
и
добавим его в нашу задачу по минификации кода.
Вывод
В конечном счете, всё сводится к личным предпочтениям: лично я предпочитаю «node-подобный» путь в написании моих таск-файлов с gulp. И, всё же, я должен сказать, что я искренне рад тому времени, которое я провел с Grunt. Я так же хотел бы отметить, что очень важно не просто понимать, как устроены оба таск-менеджера, но и осознавать, какие именно решения и почему были приняты разработчиками за основу этих инструментов. Такой подход поможет вам узнать много нового, и сэкономит вам в дальнейшем массу времени в разработке. Что ж, если вы готовы начать — вперед, на GitHub за gulp.