YouTube

Gulp - инструмент, который берёт на себя рутину веб-разработки: собирает SCSS в CSS, сжимает код, склеивает файлы, следит за изменениями и обновляет страницу в браузере. Раньше на сайте про него был курс из двадцати с лишним коротких уроков. Я собрал всё главное в одну статью: что такое Gulp, как его установить, как устроен gulpfile.js, как подключать плагины и Browsersync. В конце - готовый рабочий файл, который можно скопировать в свой проект.

Что такое Gulp и зачем он нужен

Gulp - программа на JavaScript, которая работает в Node.js и выполняет задания, описанные вами в файле gulpfile.js. Поэтому его называют таск-менеджером: task - задание, manager - тот, кто ими управляет. Официальный сайт и документация - gulpjs.com.

Сам по себе Gulp умеет немного: взять файлы, передать их дальше и положить результат в нужную папку. Всё остальное делают плагины - отдельные пакеты npm, которые «учат» Gulp новой работе. Типовые задачи, ради которых его ставят:

  • Сборка SCSS/Sass в CSS. Браузер SCSS не понимает, его нужно превратить в обычный CSS.
  • Минификация. Убрать из CSS и JavaScript пробелы, переносы и комментарии, чтобы файлы весили меньше и страница грузилась быстрее.
  • Объединение файлов. Склеить несколько скриптов или стилей в один файл.
  • Слежка за изменениями. Сохранили файл - Gulp сам пересобрал стили и обновил страницу в браузере.

Нужен ли Gulp сейчас

Честно: для нового проекта на React, Vue или другом фреймворке Gulp обычно не берут. Сборку там делает Vite (раньше - Webpack), и он идёт вместе с шаблоном проекта. Gulp остаётся удобным там, где нет сложного JavaScript, а нужно просто собрать стили и скрипты:

  • вёрстка обычного сайта из HTML, SCSS и пары скриптов;
  • тема WordPress или другой CMS;
  • старый проект, где сборка уже настроена на Gulp и её нужно поддерживать.

Если вам достался проект с gulpfile.js, разобраться в нём по этой статье хватит.

Установка Gulp

Gulp ставится через npm, поэтому на компьютере должен быть установлен Node.js. Проверить:

node -v
npm -v

Сначала один раз ставим глобально утилиту командной строки - после этого в терминале появится команда gulp:

npm install --global gulp-cli

Дальше в папке каждого проекта создаём npm-проект и ставим сам Gulp локально, как зависимость для разработки:

cd путь/к/проекту
npm init -y
npm install --save-dev gulp

npm init -y создаёт файл package.json - в нём записываются все пакеты проекта. После установки появится папка node_modules. Проверяем, что всё на месте:

gulp --version

Команда покажет две версии: CLI (глобальная утилита) и Local (Gulp в проекте). Осталось создать в корне проекта пустой файл gulpfile.js - в нём и описываются задания.

Файл gulpfile.js и первая задача

gulpfile.js - обычный JavaScript-файл. Задание в Gulp - это обычная функция. Чтобы её можно было запустить из терминала, её экспортируют через exports:

function test(done) {
  console.log('test');
  done();
}

// Задание по умолчанию: запускается командой gulp без имени
exports.default = test;

Запускаем:

gulp

В терминале появится test. Обратите внимание на done: Gulp должен узнать, что задание закончилось. Если не вызвать done() и ничего не вернуть из функции, Gulp напишет, что задание не завершилось (Did you forget to signal async completion?). Есть и другие способы сообщить о завершении - вернуть поток файлов или Promise, о них ниже.

Несколько задач и задача по умолчанию

Заданий в файле может быть сколько угодно. Каждое, которое нужно запускать по имени, экспортируем под своим именем:

function test(done) {
  console.log('test');
  done();
}

function test2(done) {
  console.log('test2');
  done();
}

exports.test2 = test2;   // запуск: gulp test2
exports.default = test;  // запуск: gulp

Если запустить gulp test2, не экспортировав функцию, Gulp ответит, что задание test2 не найдено.

В старых проектах и примерах вы встретите другую запись - через gulp.task:

const gulp = require('gulp');

gulp.task('default', test);
gulp.task('test2', test2);

Она тоже работает, но в документации Gulp сейчас рекомендуют exports. Дальше в статье - только этот способ.

Как работает Gulp: исходники на входе, готовые файлы на выходе

Почти любое задание Gulp устроено одинаково: взять файл, обработать его и положить результат в другое место. Для этого в проекте обычно заводят две папки:

  • src (от source - источник) - исходные файлы, которые вы пишете и правите;
  • build (или dist) - готовые файлы, которые собрал Gulp и которые уходят на сайт.

Названия могут быть любыми, главное - не править руками то, что лежит в build: при следующей сборке Gulp всё перезапишет.

gulp.src, gulp.dest и pipe: копируем файлы

Самое простое задание - скопировать файл из src в build:

const { src, dest } = require('gulp');

function copyCss() {
  return src('./src/style.css')
    .pipe(dest('./build/css'));
}

exports.copyCss = copyCss;
  • src() - откуда брать файлы;
  • dest() - куда положить результат;
  • pipe() - один шаг обработки. Шагов может быть несколько подряд: собрать, сжать, переименовать. Если между src и dest ничего нет, файл просто копируется.

Здесь нет done(): функция возвращает (return) поток файлов, и Gulp сам понимает, когда он закончился. Это основной способ, так пишут почти все задания.

Чтобы не перечислять файлы по одному, в src() указывают маску:

return src('./src/**/*.scss')
  .pipe(dest('./build/css'));

*.scss - все файлы с расширением .scss, ** - в любой вложенной папке внутри src. Можно передать и массив путей, а путь с ! в начале исключает файлы: ['./src/js/*.js', '!./src/js/old.js'].

Плагины Gulp: собираем SCSS в CSS

Плагины - это пакеты npm, их ищут в разделе Plugins на сайте Gulp или прямо на npmjs.com, названия обычно начинаются с gulp-. Подключение всегда одинаковое: установить пакет командой из его описания, подключить в gulpfile.js через require и вставить шагом pipe().

Пример - сборка SCSS. Ставим плагин и сам компилятор Sass:

npm install --save-dev gulp-sass sass
const { src, dest } = require('gulp');
const sass = require('gulp-sass')(require('sass'));

function styles() {
  return src('./src/scss/**/*.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(dest('./build/css'));
}

exports.styles = styles;

on('error', sass.logError) нужен, чтобы ошибка в SCSS выводилась в терминал, а не роняла Gulp.

Ещё два плагина, которые встречаются почти в каждом проекте:

  • gulp-clean-css - сжимает CSS: .pipe(cleanCSS());
  • gulp-concat - склеивает файлы в один: .pipe(concat('main.js')).
npm install --save-dev gulp-clean-css gulp-concat

Слежка за изменениями: watch

Запускать сборку руками после каждого сохранения неудобно. Функция watch() следит за файлами и сама запускает задание, когда они меняются:

const { watch } = require('gulp');

function watchFiles() {
  watch('./src/scss/**/*.scss', styles);
}

exports.watch = watchFiles;

Запускаем gulp watch и правим стили - CSS пересобирается при каждом сохранении. Остановить - Ctrl+C.

Последовательно и параллельно: series и parallel

Задания можно объединять:

  • series(a, b) - сначала a, потом b. Нужно, когда порядок важен: например, сначала очистить папку сборки, потом собирать;
  • parallel(a, b) - одновременно. Подходит для независимых заданий, например стилей и скриптов.
const { series, parallel } = require('gulp');

exports.build = series(clean, parallel(styles, scripts));

Для очистки папки отдельный плагин не нужен - хватает встроенного модуля Node.js. Функция возвращает Promise, и Gulp ждёт его так же, как поток:

const { rm } = require('fs/promises');

function clean() {
  return rm('./build', { recursive: true, force: true });
}

Browsersync: страница обновляется сама

Browsersync - бесплатный инструмент, который поднимает локальный сервер и обновляет страницу в браузере, как только меняются файлы. Удобен при вёрстке: открываете страницу на компьютере и на телефоне в одной сети и видите правки сразу на всех устройствах, а изменения CSS подставляются даже без перезагрузки страницы.

npm install --save-dev browser-sync
const browserSync = require('browser-sync').create();

function serve() {
  browserSync.init({
    server: { baseDir: './build' }
  });
}

Чтобы стили подставлялись без перезагрузки, в конце задания styles добавляют .pipe(browserSync.stream()), а для HTML и скриптов вызывают полную перезагрузку browserSync.reload(). Как это собрать вместе - в файле ниже.

Готовый gulpfile.js

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

src/
  index.html
  scss/style.scss
  js/*.js
build/        # создаёт Gulp
gulpfile.js
package.json

Пакеты:

npm install --save-dev gulp gulp-sass sass gulp-clean-css gulp-concat browser-sync

Файл gulpfile.js:

const { src, dest, watch, series, parallel } = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const cleanCSS = require('gulp-clean-css');
const concat = require('gulp-concat');
const browserSync = require('browser-sync').create();
const { rm } = require('fs/promises');

// Очистить папку сборки
function clean() {
  return rm('./build', { recursive: true, force: true });
}

// HTML просто копируем
function html() {
  return src('./src/*.html')
    .pipe(dest('./build'));
}

// SCSS -> CSS, сжатие, подстановка в браузер без перезагрузки
function styles() {
  return src('./src/scss/**/*.scss')
    .pipe(sass().on('error', sass.logError))
    .pipe(cleanCSS())
    .pipe(dest('./build/css'))
    .pipe(browserSync.stream());
}

// Все скрипты - в один файл
function scripts() {
  return src('./src/js/**/*.js')
    .pipe(concat('main.js'))
    .pipe(dest('./build/js'));
}

function reload(done) {
  browserSync.reload();
  done();
}

// Сервер и слежка за файлами
function serve() {
  browserSync.init({ server: { baseDir: './build' } });
  watch('./src/scss/**/*.scss', styles);
  watch('./src/js/**/*.js', series(scripts, reload));
  watch('./src/*.html', series(html, reload));
}

const build = series(clean, parallel(html, styles, scripts));

exports.clean = clean;
exports.build = build;               // gulp build - собрать проект
exports.default = series(build, serve); // gulp - собрать и работать с автообновлением

Команды:

  • gulp - собрать проект, открыть его в браузере и дальше пересобирать при каждом сохранении;
  • gulp build - только собрать готовые файлы в build, например перед выкладкой на сайт.

Нужна помощь с сайтом - доработка, перенос на другой хостинг, переход на PHP 8, чистка от вирусов: мои услуги.