Кратко
СкопированоGulp (англ. «глоток» [/gʌlp/, /галп/]) — менеджер для организации и выполнения задач при разработке приложений с использованием платформы Node.js.
Gulp может выполнять разные задачи: формировать файлы стилей, объединять и минифицировать файлы, оптимизировать изображения, транспилировать код и т. д. При выполнении этих задач происходит взаимодействие с рабочим окружением: обратиться к файловой системе, создать или сохранить итоговый файл, запустить процесс локального web-сервера, показать результат в браузере.
Как понять
СкопированоGulp — это менеджер задач. Он выполняет объявленные инструкции в указанной последовательности. Gulp не предоставляет разработчику готовые механизмы для сборки проекта, но позволяет настроить такую сборку с помощью необходимых модулей.
Часто в своих проектах разработчики используют Webpack, но это — сборщик модулей для приложений, написанных на JavaScript. Он «из коробки» предлагает решения для сборки проектов с использованием уже подготовленных механизмов и настроек. Подробнее об этом инструменте читайте в нашей статье про Webpack.
Модули и задачи
СкопированоПри работе с web-проектом разработчику необходимо выполнять ряд повторяющихся операций:
- проверять HTML-разметку и CSS-правила;
- преобразовывать синтаксис CSS-препроцессоров в «чистый» CSS;
- проверять JS-код на соответствие требуемым стандартам;
- при необходимости объединять несколько файлов (CSS или JS) в один;
- минифицировать большие файлы;
- проверять в браузере результат, желательно по принципу «live-reload» («живая перезагрузка»);
- формировать итоговую сборку проекта.
С использованием Gulp, выполнение всех этих задач можно организовать с применением специальных модулей, которые распространяются средствами пакетных менеджеров.
Все инструкции по организации работы проекта указываются в файле gulpfile.js. Сначала объявляются основные свойства, которые Gulp использует при выполнении задач:
const { src, dest, parallel, series, watch } = require('gulp')
const { src, dest, parallel, series, watch } = require('gulp')
src
выполняет чтение исходных файлов;( ) dest
выполняет запись итоговых файлов;( ) parallel
объединяет задачи для выполнения в параллельном режиме;( ) series
объединяет задачи для выполнения в последовательном режиме;( ) watch
запускает необходимые задачи при изменениях в файлах.( )
Потом таким же способом подключаются необходимые npm-модули, например:
const sass = require('gulp-sass')(require('sass'))const browserSync = require('browser-sync').create()const autoprefixer = require('gulp-autoprefixer')
const sass = require('gulp-sass')(require('sass')) const browserSync = require('browser-sync').create() const autoprefixer = require('gulp-autoprefixer')
Затем создаются рабочие задачи со следующей структурой:
- указывается расположение исходных файлов;
- с использованием оператора
pipe
выстраивается последовательность выполнения операций;( ) - определяется место, куда будут сохраняться итоговые файлы.
Чтобы к такой задаче можно было обратиться в любом месте gulpfile.js, ей обязательно присваивается своё название.
Задача — task
СкопированоПодготовим инструкцию для обработки файла стилей, созданных с использованием CSS-препроцессора SASS, для которой установим следующую последовательность действий:
- обратиться к исходному файлу в папке
/src
;/ styles / - преобразовать синтаксис SASS в стандартный CSS;
- вывести в терминале информацию о наличии ошибок в исходном файле;
- сохранить итоговый файл в папку
/public
;/ styles / - применить с помощью модуля
browser
новые стили для открытой в браузере страницы.- sync
До третьей версии Gulp задачи создавались встроенной функцией gulp
:
gulp.task('styles', function() { return src('./src/styles/style.scss') .pipe(sass().on('error', sass.logError)) .pipe(dest('./public/styles/style.css')) .pipe(browserSync.stream())})
gulp.task('styles', function() { return src('./src/styles/style.scss') .pipe(sass().on('error', sass.logError)) .pipe(dest('./public/styles/style.css')) .pipe(browserSync.stream()) })
Эта функция регистрирует объявленную инструкцию в качестве общедоступной задачи с названием styles
, которая может взаимодействовать с рабочим окружением. Теперь такую задачу можно запускать на выполнение как отдельно — командой gulp styles
, так и в составе сводной задачи (watch
, default
и т. п.).
Задача — function
СкопированоНачиная с четвёртой версии Gulp, для создания задач рекомендуется использовать обычные функции. В этом случае задача из примера выше будет выглядеть следующим образом:
function styles() { return src('./src/styles/style.scss') .pipe(sass().on('error', sass.logError)) .pipe(dest('./public/styles/style.css')) .pipe(browserSync.stream())}
function styles() { return src('./src/styles/style.scss') .pipe(sass().on('error', sass.logError)) .pipe(dest('./public/styles/style.css')) .pipe(browserSync.stream()) }
Формирование задачи с использованием такой функции создаёт инструкцию, которая доступна только для внутреннего использования. Чтобы объявленная функция могла взаимодействовать с рабочим окружением и выполняться командой gulp
, её необходимо экспортировать в общедоступную задачу:
exports.styles = styles
exports.styles = styles
Только после этого функция станет доступной для запуска и выполнения как отдельной командой gulp styles
, так и в составе сводной задачи.
Отслеживание изменений в проекте
СкопированоПри работе над проектом разработчику необходимо видеть результат изменений, которые он вносит в файлы. Для этой цели в Gulp предусмотрен метод watch
, который проверяет файлы при их сохранении и запускает соответствующие задачи.
Для рассмотренных выше примеров задача отслеживания изменений в файлах стилей будет выглядеть так:
- указывается расположение файлов, которые должны отслеживаться;
- вызывается задача
styles
для обработки этих файлов; - при сохранении изменений выполняется перезагрузка открытой в браузере страницы.
function watch_dev() { watch('./src/styles/style.css', styles).on( 'change', browserSync.reload )}
function watch_dev() { watch('./src/styles/style.css', styles).on( 'change', browserSync.reload ) }
Работа с проектом и его сборка
СкопированоЗапуск проекта в режиме разработки осуществляется сводной задачей default
, название которой зарезервировано в Gulp. В примере ниже она будет выполнять и отслеживать в параллельном режиме задачи, ранее экспортированные из функций:
exports.default = parallel( styles, scripts, pages, watch_dev)
exports.default = parallel( styles, scripts, pages, watch_dev )
Поскольку задача default
для Gulp является задачей по умолчанию, запустить её можно командой gulp
.
Для сборки проекта обычно создаётся отдельная задача с произвольным названием, которая последовательно выполняет необходимые действия:
exports.build = series( styles, scripts, pages)
exports.build = series( styles, scripts, pages )
После этого сборка итоговой версии проекта осуществляется вводом в терминале команды gulp build
.
Использование
СкопированоРассмотрим вариант проекта по разработке сайта.
Структура проекта будет организована с использованием компонентного подхода: для каждого блока страницы выделяется отдельная папка. В папке компонента будут располагаться его файлы стилей, разметки и скриптов. В отдельных папках будут храниться изображения и файлы шрифтов.
Все исходные файлы размещаются в папке src, результат будет сохраняться в папку public:
. ├─ /public │ ├─ /css │ ├─ /images │ ├─ /js │ ├─ index.html │ └─ order.html ├─ /src │ ├─ /components │ │ ├─ /header │ │ │ ├─ header.html │ │ │ └─ header.scss │ │ ├─ /offer │ │ │ ├─ offer.html │ │ │ ├─ offer.js │ │ │ └─ offer.scss │ │ ├─ /order │ │ │ ├─ order.html │ │ │ ├─ order.js │ │ │ ├─ order.scss │ │ └─ /footer │ │ ├─ footer.html │ │ ├─ footer.js │ │ └─ footer.scss │ ├─ /fonts │ ├─ /images │ ├─ /js │ │ └─ script.js │ ├─ /pages │ │ ├─ index.html │ │ └─ order.html │ └─ /styles │ └─ style.scss ├─ gulpfile.js └─ package.json
Сначала в рабочей папке командой npm init
необходимо выполнить инициализацию проекта, в процессе которой будет предложено указать его данные:
После этого в папке будет создан файл манифеста проекта package.json, в котором также будет сохраняться информация об используемых модулях. Модули устанавливаются стандартной командой npm install
.
Нам понадобятся следующие npm-модули:
del
для очистки папкиpublic
;sass
иgulp
для использования препроцессора SASS;- sass gulp
добавляет необходимые вендорные префиксы CSS;- autoprefixer gulp
группирует все media-запросы CSS в одном месте итогового файла стилей;- group - css - media - queries gulp
подключает отдельные файлы компонентов в итоговые файлы HTML и JS;- include browser
создаёт и запускает локальный веб-сервер.- sync
В gulpfile.js сначала объявим все необходимые свойства и модули:
const { src, dest, parallel, series, watch } = require('gulp')const del = require('del')const sass = require('gulp-sass')(require('sass'))const autoprefixer = require('gulp-autoprefixer')const gcssmq = require('gulp-group-css-media-queries')const includeFiles = require('gulp-include')const browserSync = require('browser-sync').create()
const { src, dest, parallel, series, watch } = require('gulp') const del = require('del') const sass = require('gulp-sass')(require('sass')) const autoprefixer = require('gulp-autoprefixer') const gcssmq = require('gulp-group-css-media-queries') const includeFiles = require('gulp-include') const browserSync = require('browser-sync').create()
Подготовим задачу для создания модулем browser
веб-сервера с использованием свойств API Browsersync:
- инициализируем веб-сервер;
- указываем рабочую папку;
- упрощаем ввод в браузере адреса страницы — без расширения
.html
; - назначаем номер порта для взаимодействия с веб-сервером;
- назначаем номер порта для пользовательского интерфейса веб-сервера;
- открываем в браузере главную страницу сайта.
function browsersync() { browserSync.init({ server: { baseDir: './public/', serveStaticOptions: { extensions: ['html'], }, }, port: 8080, ui: { port: 8081 }, open: true, })}
function browsersync() { browserSync.init({ server: { baseDir: './public/', serveStaticOptions: { extensions: ['html'], }, }, port: 8080, ui: { port: 8081 }, open: true, }) }
Задача по формированию CSS-стилей выполняет следующие операции:
- обращается к исходному файлу
style
;. scss - переводит синтаксис SASS в стандартный CSS;
- показывает в терминале информацию о наличии ошибок в исходном файле;
- с использованием Autoprefixer добавляет вендорные префиксы CSS, в т.ч. для работы Grid Layout в браузере IE;
- группирует вместе все медиавыражения и размещает их в конце файла;
- сохраняет итоговый файл в папку
/public
;/ css / - с помощью модуля
browser
применяет новые стили для открытой в браузере страницы.- sync
function styles() { return src('./src/styles/style.scss') .pipe(sass().on('error', sass.logError)) .pipe(autoprefixer({ grid: true })) .pipe(gcssmq()) .pipe(dest('./public/css/')) .pipe(browserSync.stream())}
function styles() { return src('./src/styles/style.scss') .pipe(sass().on('error', sass.logError)) .pipe(autoprefixer({ grid: true })) .pipe(gcssmq()) .pipe(dest('./public/css/')) .pipe(browserSync.stream()) }
Для формирования файлов скриптов создадим задачу, в которой укажем следующую последовательность действий:
- обратиться к исходному файлу
script
;. js - обработать JS-файлы компонентов;
- применить изменения для открытой в браузере страницы.
function scripts() { return src('./src/js/script.js') .pipe( includeFiles({ includePaths: './src/components/**/', }) ) .pipe(dest('./public/js/')) .pipe(browserSync.stream())}
function scripts() { return src('./src/js/script.js') .pipe( includeFiles({ includePaths: './src/components/**/', }) ) .pipe(dest('./public/js/')) .pipe(browserSync.stream()) }
Использование параметра include
для модуля gulp
позволяет подключать в script.js файлы компонентов без указания полного пути к ним:
//=include offer.js//=include order.js
//=include offer.js //=include order.js
Этот параметр будет использоваться и при обработке страниц, в результате чего таким же образом в HTML-файлах можно будет использовать файлы компонентов:
<!--=include header.html --><!--=include offer.html --><!--=include order.html --><!--=include footer.html -->
<!--=include header.html --> <!--=include offer.html --> <!--=include order.html --> <!--=include footer.html -->
Страницы сайта будут обрабатываться задачей pages
в следующей последовательности:
- считывается исходный файл страницы;
- обрабатываются HTML-файлы компонентов;
- после сохранения изменений выполняется перезагрузка открытой в браузере страницы.
function pages() { return src('./src/pages/*.html') .pipe( includeFiles({ includePaths: './src/components/**/', }) ) .pipe(dest('./public/')) .pipe(browserSync.reload({ stream: true, }))}
function pages() { return src('./src/pages/*.html') .pipe( includeFiles({ includePaths: './src/components/**/', }) ) .pipe(dest('./public/')) .pipe(browserSync.reload({ stream: true, })) }
Для использования шрифтов и картинок создадим задачи, которые в асинхронном режиме будут копировать файлы из папки src
в папку public
:
function copyFonts() { return src('./src/fonts/**/*') .pipe(dest('./public/fonts/'))}function copyImages() { return src('./src/images/**/*') .pipe(dest('./public/images/'))}async function copyResources() { copyFonts() copyImages()}
function copyFonts() { return src('./src/fonts/**/*') .pipe(dest('./public/fonts/')) } function copyImages() { return src('./src/images/**/*') .pipe(dest('./public/images/')) } async function copyResources() { copyFonts() copyImages() }
Перед запуском проекта в режиме разработки, а также перед его сборкой, желательно удалить папку public
c предыдущими версиями файлов, поэтому добавим задачу очистки:
async function clean() { return del.sync('./public/', { force: true })}
async function clean() { return del.sync('./public/', { force: true }) }
Осталось создать задачу, которая для отслеживания изменений в файлах будет выполнять следующие действия:
- следить за файлами, расположенными в указанных папках;
- запускать задачи для обработки указанных файлов;
- перезагружать открытую страницу после сохранения изменений.
function watch_dev() { watch(['./src/js/script.js', './src/components/**/*.js'], scripts) watch(['./src/styles/style.scss', './src/components/**/*.scss'], styles).on( 'change', browserSync.reload ) watch(['./src/pages/*.html', './src/components/**/*.html'], pages).on( 'change', browserSync.reload )}
function watch_dev() { watch(['./src/js/script.js', './src/components/**/*.js'], scripts) watch(['./src/styles/style.scss', './src/components/**/*.scss'], styles).on( 'change', browserSync.reload ) watch(['./src/pages/*.html', './src/components/**/*.html'], pages).on( 'change', browserSync.reload ) }
В конце gulpfile.js экспортируем объявленные функции в общедоступные задачи, после чего создадим сводные задачи для запуска проекта в режиме разработки и сборки его итоговой версии:
exports.browsersync = browsersyncexports.clean = cleanexports.scripts = scriptsexports.styles = stylesexports.pages = pagesexports.copyResources = copyResourcesexports.default = parallel( clean, styles, scripts, copyResources, pages, browsersync, watch_dev)exports.build = series( clean, styles, scripts, copyResources, pages)
exports.browsersync = browsersync exports.clean = clean exports.scripts = scripts exports.styles = styles exports.pages = pages exports.copyResources = copyResources exports.default = parallel( clean, styles, scripts, copyResources, pages, browsersync, watch_dev ) exports.build = series( clean, styles, scripts, copyResources, pages )
Заключение
СкопированоЕсли вы работаете над web-проектом, который:
- не основан на современных JS-фреймворках;
- не является SPA или PWA.
Gulp — ваш бро, не сомневайтесь.