0,0
рейтинг
13 января 2014 в 13:42

Разработка → GulpJS — фантастически быстрый сборщик проектов

Gulp.js это потоковый сборщик проектов на JS. Он использует Stream и действительно является очень быстрым. Для примера у меня есть проект где около тысячи stylus файлов, GruntJS нужно примерно 2.5 секунды на сборку и 2 секунды на обработку autoprefixer'ом. Gulp все это делает за 0.5 секунды выигрывая у GruntJS минимум в 4 раза.



Вас может отпугнуть молодость проекта, отсутствие плагинов и небольшое сообщество. Но это не так, на данный момент ведется активная разработка проекта, написано достаточное количество плагинов под задачи для популярных инструментов. На данный момент существует 165 плагинов, посмотреть их вы можете тут.

В этой статье будет больше практики, мы соберем среду разработки фронтенда используя Jade и Stylus, запустим локальный сервер и подключим Livereload. Проект я выложил на Github, экспериментируйте.


Установка Gulp

У вас должен быть установлен Node.JS и npm.
Создадим директорию проекта, создадим структуру каталогов и установим Gulp и необходимые плагины.

Струтура проекта:

|--/assets // Компоненты
|--|--/template
|--|--/stylus
|--|--/js
|--|--/img
|--/build // Каталог релиза
|--/public // Каталог разработки
|--package.json
|--gulpfile.js


Установка:

$ mkdir assets public build assets/js assets/img assets/stylus assets/template
$ touch gulpfile.js
$ sudo npm install gulp -g
$ npm init
$ npm install gulp gulp-jade gulp-stylus gulp-livereload gulp-myth gulp-csso gulp-imagemin gulp-uglify gulp-concat connect --save-dev


В корне проекта есть файл конфигурации gulpfile.js его и будем редактировать.

Иницилизируем плагины:

var lr = require('tiny-lr'), // Минивебсервер для livereload
    gulp = require('gulp'), // Сообственно Gulp JS
    jade = require('gulp-jade'), // Плагин для Jade
    stylus = require('gulp-stylus'), // Плагин для Stylus
    livereload = require('gulp-livereload'), // Livereload для Gulp
    myth = require('gulp-myth'), // Плагин для Myth - http://www.myth.io/
    csso = require('gulp-csso'), // Минификация CSS
    imagemin = require('gulp-imagemin'), // Минификация изображений
    uglify = require('gulp-uglify'), // Минификация JS
    concat = require('gulp-concat'), // Склейка файлов
    connect = require('connect'), // Webserver
    server = lr();


Задачи:

Теперь создадим первую задачу
// Собираем Stylus
gulp.task('stylus', function() {
    gulp.src('./assets/stylus/screen.styl')
        .pipe(stylus({
            use: ['nib']
        })) // собираем stylus
    .on('error', console.log) // Если есть ошибки, выводим и продолжаем
    .pipe(myth()) // добавляем префиксы - http://www.myth.io/
    .pipe(gulp.dest('./public/css/')) // записываем css
    .pipe(livereload(server)); // даем команду на перезагрузку css
});

В Gulp мы работаем с потоком, поэтому получаем данные из gulp.src и поточно обрабатываем их.

Так же создадим задачи по обработке Jade, изображений и JS
// Собираем html из Jade

gulp.task('jade', function() {
    gulp.src(['./assets/template/*.jade', '!./assets/template/_*.jade'])
        .pipe(jade({
            pretty: true
        }))  // Собираем Jade только в папке ./assets/template/ исключая файлы с _*
        .on('error', console.log) // Если есть ошибки, выводим и продолжаем
    .pipe(gulp.dest('./public/')) // Записываем собранные файлы
    .pipe(livereload(server)); // даем команду на перезагрузку страницы
}); 



// Собираем JS
gulp.task('js', function() {
    gulp.src(['./assets/js/**/*.js', '!./assets/js/vendor/**/*.js'])
        .pipe(concat('index.js')) // Собираем все JS, кроме тех которые находятся в ./assets/js/vendor/**
        .pipe(gulp.dest('./public/js'))
        .pipe(livereload(server)); // даем команду на перезагрузку страницы
});



// Копируем и минимизируем изображения

gulp.task('images', function() {
    gulp.src('./assets/img/**/*')
        .pipe(imagemin())
        .pipe(gulp.dest('./public/img'))

});




Для комфортной разработки создадим локальный сервер
// Локальный сервер для разработки
gulp.task('http-server', function() {
    connect()
        .use(require('connect-livereload')())
        .use(connect.static('./public'))
        .listen('9000');

    console.log('Server listening on http://localhost:9000');
});

Необходимые нам выше задачи предназначены для разработки и конечно хочется отслеживать изменения файлов и иметь на сервере Livereload
Для этого создадим задачу 'watch'.

// Запуск сервера разработки gulp watch
gulp.task('watch', function() {
    // Предварительная сборка проекта
    gulp.run('stylus');
    gulp.run('jade');
    gulp.run('images');
    gulp.run('js');

    // Подключаем Livereload
    server.listen(35729, function(err) {
        if (err) return console.log(err);

        gulp.watch('assets/stylus/**/*.styl', function() {
            gulp.run('stylus');
        });
        gulp.watch('assets/template/**/*.jade', function() {
            gulp.run('jade');
        });
        gulp.watch('assets/img/**/*', function() {
            gulp.run('images');
        });
        gulp.watch('assets/js/**/*', function() {
            gulp.run('js');
        });
    });
    gulp.run('http-server');
});


Теперь можно запустить наш проект и посмотреть, что получилось.
$ gulp watch


Сервер доступен по адресу localhost:9000 Мы создали среду для веб-разработке проектов с помощью Stylus и Jade с Livereload. Теперь нужно собрать оптимизированный проект. Для этого создадим задачу 'build'

Сборка проекта
gulp.task('build', function() {
    // css
    gulp.src('./assets/stylus/screen.styl')
        .pipe(stylus({
            use: ['nib']
        })) // собираем stylus
    .pipe(myth()) // добавляем префиксы - http://www.myth.io/
    .pipe(csso()) // минимизируем css
    .pipe(gulp.dest('./build/css/')) // записываем css

    // jade
    gulp.src(['./assets/template/*.jade', '!./assets/template/_*.jade'])
        .pipe(jade())
        .pipe(gulp.dest('./build/'))

    // js
    gulp.src(['./assets/js/**/*.js', '!./assets/js/vendor/**/*.js'])
        .pipe(concat('index.js'))
        .pipe(uglify())
        .pipe(gulp.dest('./build/js'));

    // image
    gulp.src('./assets/img/**/*')
        .pipe(imagemin())
        .pipe(gulp.dest('./build/img'))

});



Запускаем и получаем готовый проект в папке build
$ gulp build

Попробуйте GulpJS и начните уже использовать по-настоящему быстрые вещи в своих проектах.
Игорь Владимирович @exdeniz
карма
8,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (48)

  • 0
    Эх) хотел написать о нем статью, да не успел =)
  • +1
    Что-то мне, со своим MSBuild-ом, уже как-то и неловко. Grunt, Yeoman, и теперь, вот, еще и Gulp.
  • +1
    Щупаю его уже дня три, все очень, сказал бы, Nodejs-style. Прощай grunt.
  • +14
    Теперь будет еще одна тема для холиваров: Gulp vs Grunt
  • +4
    Думаю, хороший конкурент заставит Grunt напрячь булки и запилить оптимизацию.
  • 0
    С одной стороны в Grunt хватает всего, но с другой — Gulp — свежий глоток воздуха и более приятный API. В сети идут постоянные холивары по теме, не пойму из за чего — хорошо, когда есть конкуренция, мы все от этого выигрываем. Отправлять Grunt на свалку, разумеется, рано — плагинов для него куда больше, но Gulp выглядит вполне достойно.
  • 0
    blog.ponyfoo.com/2014/01/09/gulp-grunt-whatever — без этой ссылки пост был бы неполным.
    • 0
      Да, наверно. Там много холиварного, особенно про 2000 плагинов чуть-ли не через строчку. В плагинах Grunt можно копаться, копаться и еще раз копаться. В конце концов отмести 1800, как бесполезные для тебя и двигаться дальше.
      На самом деле надо просто перестать сравнивать их, они разные и начать использовать хоть что-то из них.
  • 0
    с виду принципиальных отличий от grunt нету… нужно пробовать… с ходу все что нужно из плагинов есть либо легко прикручивается…
  • –1
    Не понимаю, где может быть так важна скорость сборки проектов, чтобы переписывать grunt? Это же одноразовая операция.
    • +2
      Почему одноразовая? Я к примеру использую ватчеры для ребилда приложения, и мне хочется что бы это все выполнялось так быстро, насколько это возможно, что бы пока я переключаюсь из IDE в браузер, сборка уже отработала и livereload хотя бы начал перезагружать страницу.
      • 0
        Тогда ситуация противоречивая. Если у вас небольшой проект — то сборка на гранте не должна создавать ощутимых задержек при сборке.
        Если проект большой — то переход на gulp займет больше времени, чем gulp сможет сэкономить за все время его использования в качестве моментального сборщика.
        • 0
          Несомненным плюсом gulp-а является работа через текстовые потоки. Во первых потому что это эдакий unix true way, и во вторых, нет необходимости в промежуточных файлах (эту проблему я в grunt сходу решить не смог, и иногда приходится выдумывать «куда же положить файлик обработанный ngmin перед тем как скормить его google-closure...). Мелочь а приятно.

          По поводу масштабов проекта: я обычно разделяю проект на отдельные модули и собираю только измененную часть. Целиком проект собирается только раз, перед стартом ватчеров.
          • 0
            А зачем промежуточный файл? Почему финальный файл не обрабатывать closure-compiler и пр?
            • 0
              Ну промежуточный файл все равно же создается… Либо тогда отказаться вовсе от grunt (все же все эти штуки доступны в виде простых консольных команд) и настроить сборку через ant с использованием тех же текстовых потоков.
        • 0
          Создает, если есть последовательная обработка файлов. Например Stylus > autoprefixer и т.п.
  • 0
    А как в нем source maps? Есть ли плагин для browserify?
    • 0
      Для browserify плагин есть, а что имеется ввиду под «в нем source map есть»?
    • 0
      source map должен быть у минификаторов, а никак у сборщика.
      • 0
        Browserify поддерживает source maps для маппинга собранного через browserify файла в исходные js-файлы.
        • 0
          и все же причем тут grunt/gulp? пусть себе Browserify поддерживает source maps, он же доступен как модуль для npm, так что никаких ограничений gulp наложить не может.
          • 0
            Grunt и Gulp — task-менеджеры (или task-runner-ы). Под «сборщиком» я имел ввиду Browserify, поэтому неправильно понял вашу мысль в комментарии. Разумеется Gulp никаких ограничений наложить не может — вы в любом случае можете использовать Browserify и другие библиотеки, даже если у вас нет специального плагина для этого.
  • 0
    Кто-то сравнивал с brunch.io?
    cast paulmillr
    • 0
      Ну это же это более узкая утилита, что бы с ней сравнивать.
      • 0
        Да, бранч решает чисто фронтэндовские проблемы. Галп подойдет много для чего.
    • +1
      Та же тема, что и грант, только значительно чище и проще. Однозначно лучше гранта.

      Но проблемы, которые решаются бранчем оно не решает.

      Например, нет инкрементальной компиляции. И я не вижу как это с их архитектурой можно сделать. То есть при изменении одного файла, у них запустится пайплайн компиляции без поддержки кэшей и тд — медленно если файлов много.

      Для сложных проектов все еще нужно много настраивать. Например, «взять все файлы с bower, отсортировать и сгруппировать по зависимостям, добавить в очередь компиляции перед остальными файлами проекта, все свалить в один файл» — такое было бы не очень просто.

      Или даже банально — есть файлы coffeescript и jade. Как их скомпилять в один файл? Вижу как сделать два таска которые будут вотчить a) *.coffee b) *.jade и компилять в разные файлы, но не в один. Разве что через какую-то промежуточную функцию / таск.
      • 0
        Для инкрементальной есть плагин — github.com/floatdrop/gulp-watch который решает это.

        Да соединения файлов можно использовать github.com/wearefractal/gulp-concat который тоже решит это.

        А так конечно, brunch.io, mimosa.io это инструменты простые, но имеющие меньшую гибкость.
      • 0
        То есть при изменении одного файла, у них запустится пайплайн компиляции без поддержки кэшей и тд — медленно если файлов много.


        я уже писал про проект на ~1000 файлов, реально сборка их и обработка Myth составляет 0.5 секунды.
        • 0
          Миф используют, думаю, 0.01-0.1% людей от всех кто юзает альтернативные CSS-препроцессоры. Так что он не особо релевантен.

          Медленный сасс сегодня более актуален. Если через галп-вотч можно валить инкрементальные билды, то четко — надо поглядеть
          • 0
            Я просто отказался от сасс в пользу stylus и ничего не могу сказать по этому поводу. Но бродят твиты twitter.com/markgdyr/status/422485258471505920, где сборку с 24 секунд сократили до 0.9
  • 0
    Не нашел описания как использовать coffeescript в том же gulpfile… Он так может?
    • 0
      А что значит использовать coffeescript? В плане собирать, то да. Для gulpfile.js не знаю…
      • 0
        В плане gulpfile.coffee использовать вместо gulpfile.js и писать на coffeescript сам gulpfile. Я так понял, что это сейчас не возможно, это минус(
        Кстати, я так понимаю стандартный плагин gulp-stylus не может мне собирать blocks.styl в style.css?
        • 0
          Не понял зачем это?

          gulp.task('stylusIE', function () {
            gulp.src('static/b/**/*.ie.styl')
              .pipe(plumber())
              .pipe(stylus())
              .pipe(concat("screen.ie.css"))
              .pipe(myth())
              .pipe(gulp.dest('static/css/'))
              .pipe(livereload(server));
          });
          

          Вот такая конструкция собирает все файлы для ie из всех папок
          После .pipe(concat(«screen.ie.css»)) делаем один файл и дальше уже обрабатываем.

          gulp.task('stylus', function () {
            gulp.src('static/b/blocks.styl')
              .pipe(plumber())
              .pipe(stylus())
              .pipe(myth())
              .pipe(rename("screen.prefix.css"))
              .pipe(gulp.dest('static/css/'))
              .pipe(livereload(server));
          });
          


          А такая по import из blocks.styl
          • 0
            Ну я может что то делаю не так.
            К примеру:
            b/header/header.styl
            b/content/content.styl
            b/footer/footer.styl

            Есть global.styl в котором все импорты из b/. Я хочу, чтобы у меня в build/css лежал один styles.css.
            • 0
              Ну я же привел примеры.
              gulp.src('static/b/blocks.styl') для сборки из import
              gulp.src('static/b/**/*.styl') для сборки всех файлов без import
            • +1
              gulp.task('stylus', function () {
                gulp.src('./b/global.styl')
                  .pipe(plumber())
                  .pipe(stylus())
                  .pipe(rename("style.css"))
                  .pipe(gulp.dest('./build/css'))
                  .pipe(livereload(server));
              });
              


              Конкретно под твой пример, не забудь только проинициализировать модули.
        • 0
          А и да что бы использовать coffee для файла конфигурации запускаем

          gulp watch --require coffee-script

          или создаем
          gulpfile.js

          в котором
          require('coffee-script');
          require('./gulpfile.coffee');
          
          • 0
            Спасибо!
  • 0
    Думаю, автору будет приятно узнать, что я регулярно использую эту статью как шпаргалку, и рекомендую её друзьям.
    Спасибо за материал.
    • 0
      Пожалуйста. Надо на самом деле обновить будет.
    • 0
      Нашел как-то такой генератор quenchjs.com
  • –1
    Почему он так через задницу устанавливается?

    > npm install gulp gulp-jade gulp-stylus gulp-livereload gulp-myth gulp-csso gulp-imagemin gulp-uglify gulp-concat connect --save-dev

    Почему не просто npm install gulp? Кто-нибудь может объяснить? И почему на оффсайте этого не написано? Как я должен был догадаться?
    • 0
      потому что помимо gulp в этом случае ставится еще пачка плагинов. Грубо говоря так будет понятнее:

      npm install --save-dev \
          gulp \
          gulp-jade \
          gulp-stylus \
          gulp-livereload \
          gulp-myth \
          gulp-csso \
          gulp-imagemin \
          gulp-uglify \
          gulp-concat \
          connect
      
      


      Просто так проще, нежели писать пачку команд вида npm install --save-dev gulp && npm install --save-dev gulp-concat и т.д.

      К слову в списке плагинов нет sourcemaps, без него сборка не полноценна.
      • 0
        На момент написания статьи sourcemaps не было еще, он появился позже.
        • 0
          а ну да, не посмотрел на дату.

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