Современная сборка 2020 для frontend. Gulp4

  • Tutorial

Начало


Посмотрев на календарь, я понял, что уже 2020, а посмотрев на свою сборку, которая была с 2018 года, я понял, что пора её менять. В этой статье мы разберем структуру проекта, плагины (минимальный набор функционала) и их новые возможности, которые добавились за такое большое время. Мы разберем все моменты, чтобы новичок мог себе скачать эту сборку и начать с ней работать.


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


Перед тем, как вы начнете читать, хочу сказать, что материала очень много, поэтому писал только основное. Слишком очевидных вещий старался не писать, но хотел, чтобы смог понять каждый новичок. Если будут непонятные моменты, то смело переходите по ссылкам и читайте более подробную информацию, а потом снова возвращайтесь к статье. Можете задавать вопросы в комментариях, всем постараюсь ответить.


Поехали!


Gulp


Начнем с главного в нашей сборке.


Gulp — это наш фундамент, потому что он будет управлять всеми нашими задачами, другими словами, taskmanager. Концепция задач очень проста. Название асинхронной javascript функции равно названию задачи, и работает она по принципу: берет данные, трансформирует их и записывает результат.


Пример минификации сss


Как видим, все просто: называем нашу задачу css, вызываем функцию src, которая берет данные по определенному шаблону и передает данные по трубе (функция pipe()). Внутри pipe вызываем функцию плагина, которая трансформирует наши данные и результат передает функцию dest. dest — записывает результат по указанному пути.


С pipe можно выстраивать любые цепочки.


Пример


В четвертой версии много чего изменилось, но на что нужно точно обратить внимание — это на gulp.series() и gulp.parallel(). Позже мы разберем параллельное выполнение задач.


Как вы заметили, в примерах я экспортировал функции, в старом API использовали gulp.task():


gulp.task('taskName',function() {
    //do somethings
})

Больше не рекомендуется использовать gulp.task().


Экспортирование функций позволяет разделять на публичные и приватные задачи.


  • публичные задачи — экспортируются из вашего gulpfile, что позволяет запускать их командой gulp.
  • приватные задачи — создаются для внутреннего использования, как правило, в составе series() или parallel()

Пару слов о шаблонах поиска файлов в функции src.


  • templates/*.html — ищет файлы с расширением html в папке templates
  • templates/**/*.html — ищет файлы с расширением html во всех папках, которые в templates

Более подробная информация здесь, здесь и здесь.


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


После того, как мы разобрались с фундаментом нашего проекта, начнем делать первые шаги. Проверяем, установлены ли node.js и npm.


Команды в консоли


Если они не установлены, следуйте инструкциям здесь.


Создаем папку для нашего проекта. Внутри инициализируем npm npm init --yes
Ключ --yes означает автоматические ответы вопрос по проекту.


Создадим три папки:


  • build — оптимизированные файлы для использования на сервере
  • src — рабочая папка, где будут храниться все наши исходники
  • gulp — здесь будут храниться наши tasks

Еще добавим файл .gitignore, чтобы системные файлы не попадали в репозиторий.
Папка /build задокументирована, потому что часто использую GitHub Pages для демонстрации работы.


Не забудьте установить gulp: npm install --save-dev gulp


.gitignore
# Файлы и папки операционной системы
.DS_Store
Thumbs.db

# Файлы редактора
.idea
*.sublime*
.vscode

# Вспомогательные файлы
*.log*
node_modules/

# Папка с собранными файлами проекта
# build/

Первые шаги


HTML


У НTML сильно громоздкий синтаксис, и при большой вложенности тегов сложно разобрать код. Еще одна проблема в том, что многие забывают закрывать теги. Можно возразить, что сейчас умные IDE без проблем индицируют эти проблемы, но, как правило, новички не обращают внимания, что там им говорит IDE, и еще грешат форматированием кода.


Пример немного не реалистичный, но почти такое делают

bad format html


Все наши проблемы решает Pug. Одного примера будет достаточно, чтобы понять, как его использовать. Не понимаю, почему этот плагин еще повсюду не используют.


Пример базового функционала


Новичкам советую обратить внимание еще на несколько возможностей:


  • Разделение на модули — удобно, когда используешь БЭМ: один блок — один файл. Подробнее.

Пример из документации


  • Миксины — удобно использовать для однотипных блоков. Например, карточки товаров или комментариев. Подробнее.

Пример с документации



Пример с документации


За последнее время сильно ничего не изменилось, только название с Jade на Pug. Подробнее.


Разделяем HTML


На нашем сайте будут две тестовые страницы about и index. Структура на страницах повторяется: есть блоки <footer>, <header>, <head>. Поэтому все нужно вынести в отдельные файлы модули.


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


Разберем все более подробно.


  • pages — папка для наших страниц, где в корне хранятся непосредственно страницы
  • common — хранятся общие блоки для всех страниц
  • includes — хранятся модули страниц, где внутри еще одна папка, которая соответствует названию страницы

Пройдемся по файлам:


  • layout.pug — шаблон, который хранит основную структуру, и от него наследуются все другие страницы

layout.pug


  • index.pug и about.pug — наши страницы, которые наследуются от шаблона и подключают свои контентные модули

pages/index.pug и pages/about.pug


Еще, обратите внимание, у pug есть комментарии, которые попадают в html и которые нет. Подробнее здесь.


Автоматизируем первую задачу


Установим плагин gulp-pug для компиляции наших шаблонов. Выполните в консоли команду: npm i gulp-pug
Создадим файл pug2html.js в папке gulp/tasks.


pug2html.js


Здесь все понятно: ищем файлы по указанному пути, компилируем и результат выгружаем в папку build. Еще добавим pug-linter, чтобы новички не косячили и сохраняли единый стиль написания кода. Для конфигурации создадим файл .pug-lint.json в корне проекта. Правила для линтера писал на свой вкус. Вы без проблем сможете изменить. Список правил.


Теперь подключаем нашу задачу в файле gulpfile.js.


gulpfile.js


Здесь мы создаем серию с одной таски с названием start; потом мы добавим ещё. Теперь в консоли выполните команду gulp start, и в папке build должны появиться два файла: index.html и about.html.


Еще добавим gulp-w3c-html-validator, чтобы не было нелепых ошибок. Вы, наверное, догадались, что порядок подключения плагинов c помощью pipe() очень важен. То есть перед тем, как вызвать плагин pug() для компиляции, нужно сделать валидацию плагином pugLinter(), а плагин gulp-w3c-html-validator подключаем после pug(), потому что нам нужно валидировать скомпилированный html.


Последний плагин gulp-html-bem-validator — самописный плагин, сделал на скорую руку, потому что не смог найти аналогов. Очень маленький функционал, думаю, со временем буду улучшать.


Пример работы плагина


Финальный код таски pug2html.js


Стили


Для стилей мы будем использовать Scss. Все дается по аналогии с задачей pug2html. Создаем новую папку styles и скачиваем нужные пакеты npm install node-sass gulp-sass --save-dev.
Дальше пишем задачу, как и делали раньше. Берем файлы, передаем в плагин и потом сохраняем результат.


tasks/styles.js


Дальше мы добавим вспомогательные плагины: npm i gulp-autoprefixer gulp-shorthand gulp-clean-css gulp-sourcemaps stylelint gulp-stylelint stylelint-scss stylelint-config-standard-scss stylelint-config-standard stylelint-config-htmlacademy


Пройдемся по каждому плагину:


  • gulp-autoprefixer — автоматическая расстановка префиксов для старых браузеров.
  • gulp-shorthand — сокращает стили.

Пример



Файлы styles:


Структура папки styles


global.scss


media.scss


Немного обсудим файл media.scss. Есть два варианта организации медиа-запросов.


  1. Писать медиа-запросы ко всему блоку в конце файла.
  2. Писать медиа-запросы в самом селекторе, используя @mixin и @include.

Пример второго варианта


Я поклонник второго варианта, удобно, когда все стили в одном месте, и не нужно никуда скроллить и ничего искать.


Последний шаг: подключим normalize.css. Установим командой npm install normalize.css
и добавим @import "../../../node_modules/normalize.css/normalize"; в начале файла global.scss
Зачем нужен normalize.css?


JavaScript


Все делаем так же, как и с другими тасками, только подключаем другие плагины.
Установим сначала все необходимые зависимости npm i gulp-babel @babel/core @babel/preset-env --save-dev
и зависимости для eslint npm install eslint eslint-config-htmlacademy eslint-config-standard eslint-plugin-standard eslint-plugin-promise eslint-plugin-import eslint-plugin-node --save-dev



Пример


  • gulp-terser — минификация и оптимизация javascript.

Задача script


  • eslint — мы уже знаем, что делают линтеры. Решил подключить готовые конфиги, потому что очень много разных правил, чтобы все настраивать с нуля.

.eslintrc.json


Оптимизируем картинки, копируем шрифты, делаем svg-sprite


Устанавливаем плагины npm i gulp-imageMinify gulp-svgstore
Для картинок используется банальный код, который вы уже на данном этапе без проблем можете понять.


Шрифты мы просто копируем.


Не вижу смысла объяснять, как делать svg спрайты, когда в интернете много статей. Вот одна из них.


Экономим время


Чтобы каждый раз не обновлять страницу при изменении файла, подключим browser-sync. У gulp есть встроенная функция, которая следит за изменениями и вызывает нужные нам функции. Советую зайти и почитать о возможностях browsersync. Мне очень нравится возможность открытия сайта и синхронизации прокрутки страницы на нескольких устройствах. Очень удобно верстать адаптивные сайты: открыл на компьютере, открыл на телефоне — и сразу видишь результат.


Наш локальный сервер. Задача serve


Бывает такое, что сделал опечатку, сохранил код и сборка падает с ошибкой. Нужно снова перезапускать сборку, и со временем это может начать раздражать, поэтому установим npm i gulp-plumber. Plumber будет перехватывать ошибки, и после устранения ошибки сборка восстановит работоспособность. Интегрировать его очень просто, добавляем его первым .pipe(plumber()) в наших трубопроводах цепочках pug2html и styles.


Во время разработки мы будем создавать и удалять файлы. Так как у нас live reload, то созданные файлы автоматически попадут в build. Когда чуть позже мы решим удалить файл, то он останется в папке build, поэтому сделаем еще одну задачу clean, которая будет удалять папку. Установим плагин npm install del. Del.


const del = require('del')

module.exports = function clean() {
  return del('build')
}

Главное — не забыть вызвать функцию-callback, которая сообщит gulp, что задача выполнена.


Lighthouse


Lighthouse — решение для веб-приложений и веб-страниц, которое собирает современные показатели производительности.

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


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


Скрин с реального проекта


Устанавливаем npm i --save-dev gulp-connect lighthouse chrome-launcher и создаём задачу.
Результат для каждой странички будет генерироваться в папку ./reports. Там будут 'html' файлы, они открываются автоматически, но вы сами в любой момент можете их открыть и посмотреть результат.


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


lighthouse.js


Кода многовато, но он простой. Запустили наш локальный сервер с помощью browser-sync, потом хром и в конце lighthouse, где говорим, по какому url искать наш сайт.


Копируем зависимости


В нашей команде есть правило, что все dependencies нужно загружать в репозиторий. Это было связано с тем, что иногда может пропасть интернет в стране. Вручную скачивать пакеты с сайтов и загружать их в папку не очень удобно, ещё сложно следить за версиями пакетов, и каждый раз из node_modules обновлять также не очень удобно, поэтому мы должны оптимизировать этот процесс.


gulp-npm-dist — очень хороший плагин, мне он нравится тем, что он не просто копирует всю папку модуля, а только нужные файлы. README.md, LICENSE, .gitignore и другие конфигурационные файлы не копируются.


Теперь сделаем, чтобы при изменении package.json вызывался плагин. Не вижу смысла сильно заморачиваться и следить только за изменениями объекта dependencies, поэтому будем просто следить за файлом.


NPM-скрипты


Последняя оптимизация. Часто сложно и лень запоминать консольные команды, там много параметров, вводить все эти пути занимает время,
поэтому запишем длинные команды в более краткие команды.


Рассмотрим такую ситуацию: вы скопировали большой кусок кода с постороннего ресурса, и
он не соответствует вашим правилам форматирования.
Не будете же вы всё править руками? Можно просто в консоли ввести команду, которая все исправит
за вас stylelint ./src/styles/**/*.scss --fix --syntax scss, команда длинная, поэтому запишем ее в NPM-скрипт


Добавили NPM-скрипт


Как видим на скрине, теперь в консоли можно вводить npm run stylelint-fix.


Напишем еще несколько команд:


  • npm run start — вместо gulp, привык, что любой проект у меня запускается этой командой
  • npm run build — вместо gulp build, такая же ситуация, как в прошлом пункте
  • npm run lighthouse — вместо gulp build && gulp lighthouse, сначала собираем проект, а потом уже тестируем
  • npm run test — запуск разных линтеров, хорошей практикой будет запускать перед комитом

PRE-COMMIT


Не верю я, что вы будете перед комитом запускать npm run test, даже не верю, что я буду. Поэтому скачаем husky и добавим:


"husky": {
    "hooks": {
      "pre-commit": "npm run test"
    }
  }

в package.json. Если npm run test вернет ошибку, то комит не будет сделан.


Спасибо


Очень приятно, если вы прочли всю статью и сумели принять мои мысли. Отвечу на вопросы в комментариях и жду ваших pull requests и issue. Всем приятных сборок.


Ссылка на репозиторий с тем, что у нас получилось: github.

Похожие публикации

AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 31

    0

    Надо бы начинать с nvm и .nvmrc файла.

      +5
      как будто вернулся на 4-5 лет назад). Для сборки клиента есть webpack или различные cli. Gulp лучше применять там, где заканчивается зона действия сборщика клиента
        +1
        Очень хочу перейти с gulp на webpack, но не могу, потому что очень сложно. Буду рад, если на Habr появится статья, аналогичная этой, но про webpack.
          +1
          Мне помогли вот эти видео. Дальше стало интуитивно понятно.
            +1
            Рекомендую попробовать Parcel. Он по умолчанию zero-configuration. Я перешёл, и вот уже полгода как полёт нормальный.
              0
              Если я правильно помню, parcel использует как точку входа html-файл. Я не понял, как можно прикрутить его к сайту на php. Мне нужно, чтобы сборщик просто собирал css и js, не требуя обязательного присутствия html. Есть ли какие-то best practices на эту тему для parcel?
            +1
            Там дело не в сложности, а в совсем другом подходе и в документации для тех кто уже знает как оно работает и хочет нюансы посмотреть, а так с ходу даже не понятно «куда коней запрягать».
            Я в своё время смотрел видео Ильи Кантора, однако API уже несколько раз менялся и в деталях будут отличия. Поэтому если другие видео, которые советуют — подходят и они свежее, то лучше наверное их смотреть.
            youtu.be/kLMjOd-x0aQ
              +1
              Rollup проще чем webpack, попробуйте с него. Количество строк конфигурации в разы меньше, а делает тоже самое… На примере проекта на vue.js: github.com/sergeyfilimonov/vue-youtube/blob/master/build/build.js
              +2
              Можно узнать чем Webpack (сборщик фронта) лучше Gulp (менеджер задач) в задачах которые автор упомянул в статье?
              Судя по gulp-задачам предположу, что рабочий процесс команды автора подразумевает выдачу статичного фронта. В который потом интегрируют бэкэнд.

              На мое мнение, стоит все таки начать с простого для понимания (новичками) Gulp.
              Таким образом новичок будет настраивать/учиться с Gulp в разрезе терминов:
              — принцип единственной ответственности;
              — сборщика проекта;
              — таск менеджера.

              Новичок сможет сделать сборщик фронта на Gulp, но он не сможет сделать менеджер задач на Webpack, потому что работая с Webpack вы учитесь работе с Webpack (тут я немного утрирую, каюсь, но 90% функционала Webpack все таки связанна с бандлингом js).

              Я последние лет 5 использую Gulp (все кроме js). Для сборки js Rollup.
                0
                Gulp не умеет в nesting, а это основа и суть webpack.
                  0
                  Gulp не нужно это, для этого и идет webpack (tree shaking)
                  0
                  Это как сравнивать вилку и ветер.
                  Gulp универсальный инструмент и с помощью вызываемых расширений можно получить сходный с Webpack результат. Однако Webpack вообще про другое, про то, что в случае импорта, загрузки картинки через img src= и.т.д. — давайте применять такие лоадеры, а в других каких-то случаях другие. И он как вы заметили вообще ниразу не менеджер задач. Некоторые используют Вебпак вместе с Gulp в качестве менеджера задач. Для моих персональных и рабочих нужд достаточно npm скриптов, чтобы покрыть все необходимые кейсы.
                    0
                    Мы в команде использует Gulp для сборки верстки из того же Pug,ES6,SCSS, бандлинг на Rollup добавил я (есть проекты на Vue.js/React). Написана сборка универсальная, которая подходит для всех проектов плюс/минус дописываются отдельные таски под нужны проекта (кастомные) и все.
                      0
                      И ничего не тормозит? У меня разработка сайта была 6-7 страниц, некоторое количество jquery кода, все кэшировалось. Но вот уже к финалу верстки после ctrl+s успеваешь альтабнуться в браузер еще пару секунд подождать прежде чем страница обновиться.
                        0

                        Мы полностью ушли от использования jQuery, только vanilla js (ES6) и отдельный спец занимается тестированием верстки в Lighthouse и Playwright взяли на вооружение недавно. «Тормозит» значит неверно подключены скрипты или неоптимальная стратегия сборки и оптимизации бандлов и стилей. Сейчас множество механизмов preload, prefetch, lazy-loading и тд

                          0
                          Нет, так то в верстке и оптимизации страниц у меня все круто. А вот именно тот момент, когда browserSync начинает обновлять страницу проходит некоторое кол-во времени. При этом Gulp до того как alt tab нажать говорит об успешно выполненных задачах. Ну да ладно, уже давно перешел на Webpack.
                          0

                          Да возможно не оптимизированы фото. У нас сборщик использует возможности библиотеки imgmin и так же конвертирует все в webp, подключена «ленивая» загрузка через IntersectionObserver, спрайты из svg (gulp-svgstore) и тд

                    +7

                    Не легко вам фронтендерам, нужно что то менять, потому-что прошла два года, а не потому-что нужно

                      0
                      Присоединюсь.
                      Посмотрев на календарь, я понял, что уже 2020

                      То есть это было единственной причиной? Просто потому что прошлый инструмент типа «устарел»?
                        0
                        Хотел сказать что нужно переосмыслить создание сборки в наше время, потому что многие примеры остаются на уровне компилируем sass и переносим картинки в папку build. Вполне можно использовать старую сборку, которая была создана два года назад, но она морально устарела.
                      0
                      Прекоммит хуки это зло, ну или, карго-культ, если хотите. Позволю себе оставить ссылку на свои мысли почему это так.
                        0

                        Webpack и gulp разные вещи. Webpack заточен под разработку приложения, сборку es модулей. Вебпак гораздо монструознее, он css обрабатывает через js лоадер.
                        Gulp больше заточен под традиционный фронтенд, конфиг очень простой, указали путь к css, он без всяких лоадеров сделает минификацию, автопрефиксы и всё что вам нужно. И делает это быстрее вебпака.
                        Сам использую gulp на всех проектах кроме SPA. Вот там только вебпак.

                          +2
                          Путаете народ ). gulp библиотека широко профиля чем webpack. Например предлагает манипуляции с git. webpack заточен чисто под сборку фронтенд кода, 95% необходимых задач закрывает и не только es модулей).

                          Мне интересно как это gulp делает минификацию и прочее сам по себе). Там необходимо вызывать соотвествующие обработчики, которые по сути аналоги лодеров.

                          Насчет производительности gulp или webpack ничего сказать не могу(не тестил и не изучал), но собирать фронтенд на webpack в разы приятнее и необязательно SPA)
                            0
                            Gulp — это не библиотека только… и он не делает ничего «сам по себе». Суть таск-менеджера (Gulp) в использовании других инструментов для выполнении заданий по сборке и компановке статики, не более того…
                            0
                            Я довольно долго пользовался Gulp, практически с момента его появления. Но что-то не припомню, чтобы там можно было стили грузить хочешь инлайн, хочешь как js, а хочешь отдельными файлами css.
                            +1
                            Делал так же svg-спрайты, но есть косяк, safari кропает иконку с дробным значением width: 1.234em;. Причем если их несколько(список элементов с одинаковой иконкой), то кропает последние. Если задавать только высоту, то ширина тянется как у display:block;, это не фиксится сменой свойства display. Исправить положение можно округлением значения ширины, до десятой доли с помощью sass функции гист. Но не уверен что во всех случаях поможет.
                              0

                              Забавно, сделал себе для простых проектов нечто похожее https://github.com/chekit/generator-basic-page, во многом наши решения похожи. :)

                                0
                                Смотрю, что у многих возникает вопрос уровня Android vs iOs, Windows vs Linux.
                                Скажу одно — пока вы не попробуете и то, и то на одной и той же задаче, то не поймете разницы. И задача должна быть не уровня «сверстать лендинг» или набросать страницы в паге и собрать воедино.

                                Вы попробуйте обвеситься линтингом или тестить код хотя бы на уровне установки переменных сред Node.js. И то, и то, можно делать в и там, и там, но в ходе практики окончательно слез с гальпа.

                                Пока вы смотрите и на гальп и на вебпак в разрезе конкатенации файлов и перекладывании файлов из src, в dist, то однозначно вам подойдет gulp и вебпак излишен:)
                                  0
                                  Gulp — это просто таск менеджер, который берет другие инструменты (Babel, node-sass, Pug, webpack/Rollup) и собирает статику, согласно вашей логике. Не надо сравнивать его непосредственными бандлерами Webpack/Rollup/Parcel/Fusebox/JSPM/Poi и другими. Выше уже писал — Gulp сам «ничего» не делает.
                                    0
                                    Не так давно тоже решил обновить свою gulp сборку, в итоге получилась смесь gulp+webpack, если кому вдруг будет интересно, посмотреть можно тут.

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

                                    Самое читаемое