Блог

Полезные статьи и новости о жизни WaveAccess

Системы сборки frontend-проекта: gulp, grunt и альтернативы

Таск-раннеры и системы сборки сильно ускоряют работу, автоматизируя компиляцию, тестирование и другие рутинные задачи. Как и в любой другой области, на этом рынке существует сильная конкуренция. До 2014 года среди них главенствовал таск-раннер grunt, но позже из состава проекта выделилась небольшая команда, которая решила делать альтернативный инструмент, gulp, ориентированный на сборку проекта.

Чтобы помочь вам определиться с выбором, в рамках статьи рассмотрим основные таск-менеджеры:

  • gulp 
  • grunt

а также коснемся других средств и способов сборки.

Забегая немного вперед, скажем, что мы в WaveAccess пользуемся именно gulp. Внедрить инструмент оказалось очень легко: у семейства продуктов JetBrains (IDEA, WebStorm, ReSharper), которые мы используем уже много лет, есть отличные плагины для работы с gulp/grunt и npm/nodejs.

Таск-менеджер vs. система сборки проекта: в чем разница?

Таск-менеджер — инструмент для автоматизации задач. В конфигурации раннеров можно записать имена этих задач; функцию, которая их выполняет; плагины для ускорения стандартных действий, но сами задачи могут быть произвольными. Например:

  • Задачи для деплоя (zip проекта, загрузка проекта на удаленный сервер и тп)
  • Задачи по сборке проекта (минификация, оптимизация, проверка кода на валидность и тп)
  • Задачи для миграции данных и т.д.

Примеры таких инструментов — grunt и gulp.

Система сборки — это инструмент, который решает только одну типовую задачу сборки проекта на java script, в которую входят:

  • конкатенация,
  • проверка кода на валидность,
  • минификация кода, и тд.

К подобным инструментам относятся Webpack, Broccoli, Brunch, Browserify и другие.

Все подобные frontend-задачи можно автоматически выполнять при помощи других средств: к примеру, с помощью npm run, о котором мы также поговорим в статье.

Пример

Рассмотрим gulp-файл для сборки проекта:

const gulp = require (‘gulp’); 
const coffee = require (‘gulp-coffee’); 
const concat = require (‘gulp-concat’); 
const uglify = require (‘gulp-uglify’); 
const imagemin = require (‘gulp-imagemin’); 
const sourcemaps = require (‘gulp-sourcemaps’); 
const del = require (‘del’);
} 

Но сборка — это частный случай большой типовой задачи. Для gulp можно написать и другой config — скажем, для деплоя:

var gulp = require('gulp');
var zip = require('gulp-zip');
var del = require('del');
var install = require('gulp-install');
var runSequence = require('run-sequence');
var awsLambda = require("node-aws-lambda");

gulp.task('clean', function(cb) {
  del(['./dist', './dist.zip'], cb);
});

gulp.task('copy', function() {
  return gulp.src('index.js')
    .pipe(gulp.dest('dist/'));
});

gulp.task('node-mods', function() {
  return gulp.src('./package.json')
    .pipe(gulp.dest('dist/'))
    .pipe(install({production: true}));
});

// Clean up all aws-sdk directories from node_modules. We don't
// need to upload them since the Lambda instance will already
// have it available globally.
gulp.task('clean-aws-sdk', function(callback) {
  del(['dist/node_modules/**/aws-sdk'], callback);
});

gulp.task('zip', function() {
  return gulp.src(['dist/**/*', '!dist/package.json'])
    .pipe(zip('dist.zip'))
    .pipe(gulp.dest('./'));
});

gulp.task('upload', function(callback) {
  awsLambda.deploy('./dist.zip', require("./lambda-config.js"), callback);
});

gulp.task('deploy', function(callback) {
  return runSequence(
    ['clean'],
    ['copy'],
    ['node-mods'],
    ['clean-aws-sdk'],
    ['zip'],
    ['upload'],
    callback
  );
});

A можно описывать новые задачи как комбинации уже существующих:

gulp.task(‘deploy’,
    gulp.series (‘clean’, ‘copy’, ‘node-mods’, ‘clean-aws-sdk’, ‘zip’, ‘upload’)
);

В этом и заключается отличие. Теперь рассмотрим основные инструменты.

gulp vs. grunt

Итак, перед нами два таск-раннера: gulp и grunt. Оба используют node.js и npm, а задачи им ставят, используя javascript.

Grunt

Таск-раннер, который разработан для Node.js в 2012 году. Сейчас у него около 11к плагинов — почти вдвое больше, чем у gulp. До сих пор применяется в Adobe Systems, jQuery, Twitter, Mozilla, Bootstrap, Cloudant, Opera, WordPress, Walmart и Microsoft.

Gulp

Потоковая система сборки (a streaming build system). Создан как ответвление grunt и насчитывает около 4000 плагинов.

На первый взгляд они схожи, однако у gulp есть то, что делает его более удобным именно для сборки: умение параллельно обрабатывать задачи и компактный конфиг, лаконичное API. Давайте посмотрим поближе их принцип работы.

Потоковая передача данных

Перед нами грант-файл, который осуществляет сборку и обработку CSS.  

Gruntfile

Из него видно, что grunt при запуске каждого процесса:

  1. открывает файл;

  2. запускает процесс;

  3. сохраняет изменения;

  4. закрывает обработанный файл, чтобы предотвратить вмешательство в него следующего процесса;

  5. записывает файл в итоговую папку.

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

Blog Img 6

Плагины пишут разные авторы. Чтобы каждый плагин мог работать с файлами, обходя сохранение, файлы нужно представить в виде объектов. В gulp эту задачу выполняет виртуальная файловая система Vynyl-FS. И gulp сразу передает файл следующему процессу без создания временных файлов и без сохранения на диск.

Та же самая конфигурация для gulp уже компактнее:

Gulpfile 

Его общий механизм работы — потоковая обработка файлов без записи на диск:

Blog Img 7

Последовательность выполнения задач

Есть и другое отличие: gulp по умолчанию асинхронно выполняет таски. В этом есть и плюсы, и минусы. В том же конфигурационном файле мы даем команду считать файлы из директории dev/*scss и отправить их в SASS. 

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

Последовательное выполнение задач делает gulp быстрым и мощным, но изредка возникает необходимость все же выполнить задачи синхронно, как в grunt. Проблему можно решить через обратный вызов, возвращение потока или Promise. Более подробно задача разобрана на Хабре. Есть и альтернативный вариант на самом сайте npm.js

Если вы пользуетесь grunt, но вас привлекает потоковая передача данных -- тот же модуль Vynyl-FS можно использовать для ее реализации в grunt.

API

Лаконичное API gulp имеет всего 5 методов:

  1. .task(name, fn). Регистрирует функцию с именем. Можно указать зависимость от других тасков, если нужно их выполнить сначала.  

  1. .run(tasks...). Выполняет задачи.

  1. .watch(glob, fn). Выполняет функцию, если файл на месте glob меняется.

  1. .src(glob). В качестве параметра принимает маску файлов и возвращает поток, представляющий эти  файлы. Затем поток может быть передан на вход плагинам.

  1. .dest(folder). Сохраняет файлы в указанную папку.

Особенно хотелось бы отметить наличие .watch() в “родном” API проекта, ведь слежение за постоянными изменениями файлов является важнейшей составляющей сборки. Краткость API дает возможность этому таск-менеджеру сфокусироваться на своей основной задаче – сборке проектов.

Альтернативы gulp и grunt

Несмотря на популярность gulp (больше 130 к скачиваний в день) и grunt (более 86 к скачиваний в день согласно npmjs.com), разработчики видят в этих системах и свои недостатки: к примеру, зависимость от плагинов, неполная документация, неудобный дебаггинг. В качестве альтернативы можно рассмотреть системы сборки проектов (такие как Broccoli и Webpack) или npm-скрипты.

Системы сборки проектов

Рассмотрим несколько альтернативных решений на платформе Node.js. Для сборки проекта они могут заменить gulp и grunt.

Brunch

Эта система, как и gulp, возникла как конкурент таск-раннеру grunt, однако разработчики изначально задумывали ее именно как помощник для сборки со всеми преимуществами и недостатками. Он без настроек “поймет”, что *.js — это файл со скриптами, *.coffee — это CoffeeScript; его конфиг более компактен. Однако никаких произвольных действий на этапе сборки он совершить не сможет.

Вот конфиг-файл Brunch. Он написан на CoffeeScript (можно также писать на JS):

exports.config =
  files:
    javascripts:
      joinTo:
        'javascripts/app.js': /^app/
        'javascripts/vendor.js': /^(bower_components|vendor)/
    stylesheets:
      joinTo: 'stylesheets/app.css'
      order:
        after: ['vendor/styles/helpers.css']
    templates:
      joinTo: 'javascripts/app.js'

Здесь хочется обратить внимание на операторы joinTo и order. На уровне конфига Brunch понимает, что придется собирать файлы в нужном порядке. В результате, конфиг занимает 20-30 строк.

Broccoli

Инди-инструмент, который находится на стадии разработки. Его разработчики хотели создать конкуренцию уже gulp.

По сравнению с gulp, инструмент Broccoli использует другие принципы:

  • Ускорение сборки. Каждый плагин реализует промежуточное кэширование результатов сборки вместо частичной пересборки только нужных файлов.  

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

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

Webpack

Webpack - гибкая модульная система сборки. Она обладает непривычным синтаксисом, но сама воспринимает любые синтаксисы модулей.

Понимая, что придется конкурировать с такими гигантами как gulp, создатели решили облегчить нам жизнь при разработке больших проектов. И добавили в утилиту:

  • Умение автоматически строить дерево зависимостей и ресурсов.

  • Удобные средства для реализации динамической подгрузки.

  • Совместимость с практически любыми модулями (AMD, UMD, ES 2015, Common JS, сторонние модули на их основе).

  • Совместимость с препроцессорами (SASS, Babel, Coffee Script, Type Script и т.д.).

  • Live Reload (технологию асинхронной загрузки, при которой браузер обновляет не страницы целиком, а отдельные приложения).

  • Возможность делить код и генерировать множество bundle-файлов, избегая создания одного тяжелого bundle.js.

  • Умение оптимизировать код.

Отдельно можно отметить гибкий подход к зависимостям. Модулем может стать JS, CSS и HTML-файл, и даже JPEG с PNG.  Можно использовать require(“myJSfile.js”) и require(“myCSSfile.css”), делить и использовать части артефакта повторно.

Подробнее о возможностях, конфигурации инструмента, плагинах можно найти на Github, в презентации с Fronttalks: глубокое погружение в Webpack.

npm скрипты

Задачи по сборке можно решить и при помощи npm-скриптов. Многих отпугивает эта идея: мало возможностей, скрипты недостаточно быстрые в сравнении с gulp или Webpack. Тем не менее, эти недостатки преувеличены.

Возможности npm-скриптов

Npm-скрипты решают довольно много задач. Так, например, можно реализовать скрипты ловушек:

{
 "name": "npm-scripts-example",
 "version": "1.0.0",
 "description": "npm scripts example",
 "scripts": {
   "prebuild": "echo I run before the build script",
   "build": "cross-env NODE_ENV=production webpack"
   "postbuild": "echo I run after the build script"
 }
}

Сценарии будут загружаться по порядку согласно префиксам: prebuild, например, стартует перед build, потому что у него есть префикс pre. Соответственно, postbuild будет загружен последним. Команда npm run build запустит их в нужном порядке.

Можно вызывать один скрипт из другого, чтобы декомпозировать сложные задачи. Например, здесь задача prebuild вызывает задачу clean.

{
 "name": "npm-scripts-example",
 "version": "1.0.0",
 "description": "npm scripts example",
 "scripts": {
   "clean": "rimraf ./dist && mkdir dist",
   "prebuild": "npm run clean",
   "build": "cross-env NODE_ENV=production webpack"
 }
}

Если задача становится слишком сложной, всегда можно вызвать отдельный файл:

{
 "name": "npm-scripts-example",
 "version": "1.0.0",
 "description": "npm scripts example",
 "scripts": {
   "build": "node build.js"
 }
}

За счет стриминга gulp для задач сборки стал гораздо удобнее, чем grunt. Но его можно реализовать и через npm. В Windows и Unix стриминг делается по умолчанию, без сохранения промежуточных файлов.

К примеру, в Unix можно сделать grep содержимого файла и направить его в новый файл:

grep ‘My Name’ bigFile.txt > linesThatHaveMyName.txt

Редирект(>)направляет нужные строки в конечный файл. Задача выполняется без сохранения промежуточных файлов.

Но есть и неудобства: нельзя оставлять комментарии в package.json. Выходом может стать создание коротких скриптов с понятными названиями, нацеленных на какую-то одну небольшую задачу. Более подробно вопрос замены таск-раннеров npm-скриптами хорошо освещен в англоязычной статье Why I Left Gulp and Grunt for npm Scripts.

Итог

На рынке существует большая конкуренция инструментов для автоматизации рутинных задач (например, gulp и grunt), а также инструментов для автоматизации сборки проекта (Webpack, Broccoli, Medusa, Browserify и т.д.).

Если смотреть на таск-раннеры, то gulp по сравнению с grunt более прост, понятен и производителен: выигрывает за счет экономии на дисковых операциях. Но у grunt больше плагинов (например, есть плагин для тестирования). Из-за этого у него остается много поклонников.

Если же говорить только о сборке, то у gulp есть все преимущества перед grunt:

  • Поточная архитектура для передачи файлов по цепочке, которую обеспечивает модуль Vynyl-FS.

  • По умолчанию - асинхронное выполнение задач.

  • Лаконичное API всего из 5 функций.

В то же время, для сборки Webpack является не менее интересным инструментом. В нем предусмотрена технология Live Reload, ускоряющая обновление браузера. Это огромный плюс: технология экономит время на нажатие кнопки обновления, которую разработчикам приходится нажимать постоянно. В gulp также есть Live Reload, но Webpack сложно сравнивать с gulp или grunt, так как он “заточен” только под билд и не “умеет” решать произвольные задачи.

Все эти решения прекрасно интегрируются с семейством продуктов от JetBrains, однако мы в WaveAccess предпочли именно grunt за широкие возможности и для верстальщиков, и для frontend-специалистов.

Если у Вас возникли вопросы и вам необходима разработка web-проекта, пишите нам на hello@wave-access.com

Заказать звонок

Удобное время:

Отменить

Пишите!

Присоединить
Файл не больше 30 Мб.
Отменить