Зарегистрируйтесь, чтобы продолжить обучение

Базовые функции Gulp Gulp

В прошлом уроке мы подключили встроенную в пакет Gulp функцию parallel(), которая позволила объединить несколько функций в одну единую задачу.

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

const sassCompile = (done) => {
  console.log('Compile SASS to CSS');

  done();
};

const pugCompile = (done) => {
  console.log('Compile Pug to HTML');

  done();
};

const imagesOptimize = (done) => {
  console.log('Optimize Images');

  done();
};

exports.default = parallel(sassCompile, pugCompile, imagesOptimize);

Помимо parallel() внутри пакета Gulp существует ещё несколько функций, которые будут рассмотрены в этом уроке. Нет нужды расписывать каждую функцию в отдельности, поэтому рассмотрим самые распространённые, которые вы будете использовать в работе. Больше информации про различные функции можно будет найти в документации пакета Gulp, ссылка на которую будет в конце урока.

series()

Функция series() по своему функционалу очень похожа на функцию parallel(), которую мы изучили в прошлых уроках. Но если есть две разные функции, значит у них должны быть отличия? И ответ — да! Отличие заключается в том, как именно будут вызываться функции в рамках задачи. И здесь два подхода:

  • Функция parallel(), как можно понять из названия, выполняет функции параллельно/одновременно. В примере выше функции sassCompile(), pugCompile() и imagesOptimize() не будут стоять в какой-то очереди или дожидаться окончания друг друга, а начнут работать вместе. Этот подход удобен, если задачи никак друг с другом не связаны и результат одной не зависит от результата другой.

Параллельное выполнение задач Gulp

  • В противовес параллельному выполнению есть последовательное, которое реализуется функцией series(). Смысл этой функции в том, что пока не завершилась работа одной функции не начнётся выполнение другой. Это очень важно при использовании множества подзадач, которые напрямую влияют на то, с чем будет работать следующая функция.

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

На скриншотах стоит обратить внимание на то, когда запускаются задачи. В параллельном выполнении Gulp показывает одновременный запуск всех задач, а при использовании функции series() выполнение новой задачи стартует только после окончания выполнения предыдущей.

src() и dest()

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

  • src() для указания пути к обрабатываемому файлу
  • dest() для указания пути, куда необходимо положить обработанный файл

Особых неожиданностей здесь нет. Эти функции отвечают за обработку путей к файлам и базовому взаимодействию. На самом деле они намного гибче, чем может показаться с самого начала, но остальной функционал используется крайне редко и в больших проектах, например кеширование. Посмотрите на базовый пример операции по копированию файла:

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

const copyFile = () => {
  return src('src/sass/app.scss')
    .pipe(dest('build/styles'));
};

exports.copy = copyFile;

После выполнения задачи файл src/sass/app.scss будет скопирован в директорию build/styles/

layout-project/
├── build/
│   ├── styles/
│   │   └── app.scss
├── src/
│   ├── sass/
│   │   └── app.scss
│   ├── pages/
│   │   ├── index.pug
│   │   ├── sections/
│   │   │   ├── head.pug
│   │   │   └── footer.pug
├── gulpfile.js
├── package.json
└── node_modules/

Сейчас он там не нужен, так как в директорию build попадут уже обработанные файлы, но теперь вы знаете, каким именно способом они туда попадут :)

Обратите внимание, что в примере не вызывается функция done(), как было в прошлых примерах. Это связано с тем, что мы указали ключевое слово return. Подробнее о ключевом слове return вы могли прочитать в курсе введения в программирование.

Globs

В примере выше был указан чёткий путь к файлу, который мы хотим скопировать. Для небольших проектов это может быть достаточно простым и эффективным решением, но, зачастую необходимо обработать не один, а все файлы из определённой директории или даже дерева директорий. К примеру, у нас возможна вот такая структура стилей:

layout-project/
├── src/
│   ├── sass/
│   │   ├── global.scss
│   │   ├── mobile.scss
│   │   ├── desktop.scss

Как корректно обработать три этих файла? Есть два варианта:

  1. Обработать каждый файл в отдельности
  2. Обработать все файлы внутри одной функции

С первым вариантом всё просто — создаём три функции, объединяем их в единую задачу и выполняем:

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

const copyGlobalScss = () => {
  return src('src/sass/global.scss')
    .pipe(dest('build/styles'));
};

const copyMobileScss = () => {
  return src('src/sass/mobile.scss')
    .pipe(dest('build/styles'));
};

const copyDesktopScss = () => {
  return src('src/sass/desktop.scss')
    .pipe(dest('build/styles'));
};

exports.copy = parallel(copyGlobalScss, copyMobileScss, copyDesktopScss);

Если всего файла три, то такое решение можно считать нормальным, но не хорошим. Ведь над каждым файлом производится одинаковая операция, а меняется только сам обрабатываемый файл.

Для указания нескольких файлов используются специальные шаблоны путей — Globs. Это небольшой пакет, который преобразует шаблоны в пути и по умолчанию встроен в Gulp. Достаточно изучить пару приёмов, с помощью которых вы сможете выбирать почти любые файлы и в любом количестве.

Первая конструкция — использование звёздочки *, она указывает на то, что надо выбрать всё, что не противоречит указанному в пути. Например, мы можем заменить звёздочкой имя файла в последнем примере и, тогда, Gulp выберет все три файла: global.scss, mobile.scss и desktop.scss

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

const copyScss = () => {
  return src('src/sass/*.scss')
    .pipe(dest('build/styles'));
};

exports.copy = copyScss;

Можно ещё больше упростить процесс копирования файлов. Предположим, что SASS файлы находятся в совершенно разных директориях, а мы хотим сложить их в единую. Необходимо пройтись по всем доступным директориям, проверить наличие там файла с расширением scss/sass и скопировать. Для этого используется специальная конструкция **, которая нацелена на проход по директориям. Например, можно видоизменить код следующим образом:

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

const copyScss = () => {
  return src('src/**/*.scss')
    .pipe(dest('build/styles'));
};

exports.copy = copyScss;

Теперь при запуске задачи будут проверены все директории внутри src на предмет наличия файла с расширением .scss. Здесь важно отметить, поиск будет производиться не только внутри поддиректорий src, но и в самой директории src. Например будут выбраны следующие файлы:

  • src/styles.scss
  • src/project/app/styles/app.scss
  • src/sass/mobile.scss

и так далее.


Важно: при использовании метода с поиском файлов в различных директориях, при их переносе с помощью функции dest() Gulp сохраняет ту вложенность, которая была. Например, при выполнении прошлого примера файл src/project/app/styles/app.scss окажется по пути build/styles/project/app/styles/app.scss. Это важная особенность, которую стоит помнить для избежания ошибок при работе с проектом.


Это отлично работает, но есть одна существенная проблема — внутри нашего src могут находиться директории, из которых нет необходимости обрабатывать файлы. Например, директории с npm-пакетами. Чтобы уйти от этой проблемы в Globs существует метод исключения директорий, который указывается через знак логического отрицания !. В таком случае нужные пути и исключаемые указываются в виде массива строк. Исключим из копирования директорию src/project:

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

const copyScss = () => {
  // ['src/**/*.scss', '!src/project/**'] — массив строк
  // в котором исключается директория src/project и все вложенные в неё директории
  return src(['src/**/*.scss', '!src/project/**'])
    .pipe(dest('build/styles'));
};

exports.copy = copyScss;

Важно обратить внимание, что в конце исключаемой директории стоят символы /**. Они говорят о том, что необходимо исключить не только саму директорию, но и все вложенные в неё.

Pipe()

При просмотре последних примеров может возникнуть вопрос: «Что за pipe?». Перейдём к этому методу:

Если не вдаваться в тонкие подробности, то pipe() позволяет связывать потоки чтения и записи друг с другом. Если вернуться к примеру с копированием файлов, то именно с помощью pipe() появляется возможность получить файл и отдать его для последующей обработки функции dest().

Так же будет происходить и при обработке файлов с помощью плагинов, например при компиляции SASS в CSS. Чтобы связать всю цепочку одновременно необходимо использовать pipe().

В контексте работы с пакетом Gulp можно выстроить типичную цепочку, или шаблон, задачи:

const task = () => {
  return src('файл с которым работаем')
    .pipe(pluginOne()) // Обработка первым плагином
    .pipe(pluginTwo()) // Обработка вторым плагином
    .pipe(pluginN()) // Обработка ещё каким-нибудь плагином
    .pipe(dest('путь, по которому расположим обработанный файл'));
};

Самостоятельная работа

Добавьте в учебный проект несколько директорий и файлов. Создайте задачи для их копирования и перемещения по разным директориям. Отработайте изученные Globs шаблоны


Дополнительные материалы

  1. Встроенные функции в Gulp

Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»