В прошлом уроке мы подключили встроенную в пакет 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()
не будут стоять в какой-то очереди или дожидаться окончания друг друга, а начнут работать вместе. Этот подход удобен, если задачи никак друг с другом не связаны и результат одной не зависит от результата другой.
- В противовес параллельному выполнению есть последовательное, которое реализуется функцией
series()
. Смысл этой функции в том, что пока не завершилась работа одной функции не начнётся выполнение другой. Это очень важно при использовании множества подзадач, которые напрямую влияют на то, с чем будет работать следующая функция.
На скриншотах стоит обратить внимание на то, когда запускаются задачи. В параллельном выполнении 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
Как корректно обработать три этих файла? Есть два варианта:
- Обработать каждый файл в отдельности
- Обработать все файлы внутри одной функции
С первым вариантом всё просто — создаём три функции, объединяем их в единую задачу и выполняем:
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 шаблоны
Дополнительные материалы
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.