Приятная сборка front-end проекта
В этой статье мы подробно разберем процесс сборки фронтенд проекта, который прижился в моей повседневной работе и очень облегчил рутину.
Статья не претендует на истину в последней инстанции, так как сегодня существует большое количество различных сборщиков и подходов к сборке, и каждый выбирает по вкусу. Я лишь поделюсь своими мыслями по этой теме и покажу свой workflow.
UPD (13 марта 2015): Заменил несколько плагинов на более актуальные + решил проблему с импортом CSS файлов внутрь SCSS.
Мы будем использовать сборщик Gulp. Соответственно у вас в системе должен быть установлен Node js. Установку ноды под конкретную платформу мы рассматривать не будем, т.к. это гуглится за пару минут.
И для начала отвечу на вопрос — почему Gulp? Из более или менее сносных альтернатив мы имеем Grunt и Brunch.
Когда я только начал приобщаться к сборщикам — на рынке уже были и Grunt и Gulp. Первый появился раньше и по этому имеет более большое коммьюнити и разнообразие плагинов. По данным с npm:
- Grunt — 11171 пакет
- Gulp — 4371 пакет
Но Grunt мне показался черезчур многословным. И после прочтения нескольких статей-сравнений — я предпочел Gulp за его простоту и наглядность.
Brunch — это сравнительно молодой проект, со всеми вытекающими из этого плюсами и минусами. Я с интересом наблюдаю за ним, но в работе пока не использовал.
Приступим
Создадим папку под наш проект, например «habr». Откроем ее в консоли и выполним команду:
Можно просто нажать Enter на все вопросы установщика, т.к. сейчас это не принципиально.
В итоге в папке с проектом у нас сгенерируется файл package.json, примерно такого содержания:
Немного видоизменим его под наши нужды:
В блоке dependencies мы указали что нам нужен gulp и тут же будем прописывать все наши плагины.
Плагины
- gulp-autoprefixer — автоматически добавляет вендорные префиксы к CSS свойствам (пару лет назад я бы убил за такую тулзу);
- gulp-minify — css - нужен для сжатия CSS кода;
- browser-sync — с помощью этого плагина мы можем легко развернуть локальный dev сервер с блэкджеком и livereload, а так же с его помощью мы сможем сделать тунель на наш localhost, что бы легко демонстрировать верстку заказчику;
- gulp-imagemin — для сжатия картинок;
- imagemin-pngquant — дополнения к предыдущему плагину, для работы с PNG;
- gulp-uglify — будет сжимать наш JS;
- gulp-sass — для компиляции нашего SCSS кода;
- gulp-sourcemaps — возьмем для генерации css sourscemaps, которые будут помогать нам при отладке кода;
- gulp-rigger — это просто киллер фича. Плагин позволяет импортировать один файл в другой простой конструкцией и строка при компиляции будет заменена на содержимое файла;
- gulp-watch — Будет нужен для наблюдения за изменениями файлов. Знаю что в Gulp есть встроенный watch, но у меня возникли с ним некоторые проблемы, в частности он не видел вновь созданные файлы, и приходилось его перезапускать. Этот плагин решил проблему (надеюсь в следующих версиях gulp это поправят);
- rimraf — rm -rf для ноды;
Установим все плагины и на выходе получим такой package.json:
Bower
Я уже не мыслю своей работы без пакетного менеджера Bower и надеюсь вы тоже. Если нет, то почитать о том что это и с чем его едят можно тут.
Давайте добавим его к нашему проекту. Для этого выполним в консоли команду:
Можно так же Enter на все вопросы.
В конце мы получаем примерно такой файл bower.json:
И модифицируем его до нужного нам состояния:
В блоке dependencies мы будем указывать зависимости нашего проекта. Сейчас просто для теста это normalize и jQuery (хотя я уже не помню когда начинал проект без этих вещей).
Ну и конечно установим их командой:
Ну а теперь самое интересное. Создадим структуру нашего проекта и настроим сборщик.
Структура проекта
Это очень спорный момент. Конечно проекты бывают разные, так же как и предпочтения разработчиков. Стоит только взглянуть на сайт yeoman.io (кстати это очень классный инструмент, который предоставляет большое кол-во заготовленных основ для проекта со всякими плюшками. Однозначно стоит присмотреться к нему). Мы не будем ничего выдумывать и сделаем самую простую структуру.
Для начала нам понадобится 2 папки. Одна (src) в которой мы собственно будем писать код, и вторая (build), в которую сборщик будет выплевывать готовые файлы. Добавим их в проект. Текущая структура у нас выглядит так:
В папке src создадим типичную структуру среднестатистического проекта. Сделаем main файлы в папках js/ и style/ и создадим первую html страничку такого содержания.
Структура папки src теперь будет выглядеть так:
Тут все тривиально:
- fonts — шрифты;
- img — картинки;
- js — скрипты. В корне этой папки будет только файл main.js, который пригодится нам для сборки. Все свои js файлы — надо будет класть в папку partials;
- style — стили. Тут так же в корне только main.scss, а рабочие файлы в папке partials;
- template — тут будем хранить повторяющиеся куски html кода;
- Все html страницы которые мы верстаем — будут лежать в корне src;
Добавим в partials первые js и scss файлы и напоследок — перейдем в корень нашего проекта и создадим там файл gulpfile.js. Вся папка проекта сейчас выглядит так:
Теперь все готово к настройке нашего сборщика, так что let’s rock!
Gulpfile.js
Вся магия будет заключена в этом файле. Для начала мы импортируем все наши плагины и сам gulp:
Конечно, не обязательно делать это именно так. Существует плагин gulp-load-plugins который позволяет не писать всю эту лапшу из require. Но мне нравится когда я четко вижу что и где подключается, и при желании могу это отключить. По этому пишу по старинке.
Так же создадим js объект в который пропишем все нужные нам пути, чтобы при необходимости легко в одном месте их редактировать:
Создадим переменную с настройками нашего dev сервера:
Собираем html
Напишем таск для сборки html:
Напомню, что rigger это наш плагин, позволяющий использовать такую конструкцию для импорта файлов:
Давай те же применим его в деле!
В папке src/template/ — создадим файлы header.html и footer.html следующего содержания:
а наш файл index.html изменим вот так:
Осталось перейти в консоль и запустить наш таск командой:
После того как она отработает — идем в папку build и видим там наш файл index.html, который превратился в это:
Это же просто восхитительно!
Помню как много неудобств доставляло бегать по всем сверстанным страничкам и вносить изменения в какую-то повторяющуюся на них часть. Теперь это делается удобно в одном месте.
Собираем javascript
Таск по сборке скриптов будет выглядеть так:
Помните наш файл main.js? Вся идея тут состоит в том, чтобы с помощью rigger’a инклюдить в него все нужные нам js файлы в нужном нам порядке. Именно ради контроля над порядком подключения — я и делаю это именно так, вместо того что бы попросить gulp найти все *.js файлы и склеить их.
Часто, при поиске места ошибки я по очереди выключаю какие то файлы из сборки, что бы локализовать место проблемы. В случае если бездумно склеивать все .js — дебаг будет усложнен. Заполним наш main.js:
Именно так я делаю на боевых проектах. Вверху этого файла всегда идет подключение зависимостей, ниже подключение моих собственных скриптов.
Кстати, bower пакеты можно подключать через такой плагин как gulp-bower. Но я опять же не делаю этого, потому что хочу самостоятельно определять что, где и как будет подключаться.
Осталось только запустить наш таск из консоли командой:
И в папке build/js — мы увидим наш скомпилированный и сжатый файл.
Собираем стили
Напишем задачу для сборки нашего SCSS:
Здесь все просто, но вас могут заинтересовать настройки автопрификсера. По умолчанию он пишет префиксы необходимые для последних двух версий браузеров. В моем случае этого достаточно, но если вам нужны другие настройки — вы можете найти их тут.
Со стилями я поступаю так же как и с js, но только вместо rigger’a — использую встроенный в SCSS импорт.
UPD (13 марта 2015): У некоторых людей возникла проблема с импортом css файлов инлайн. Как оказалось, gulp-sass не умеет этого делать, и на выходе дает простой CSS импорт. Но этот вопрос решает наш плагин gulp-minify-css, который заменяет CSS импорт на содержимое файла.
Наш main.scss будет выглядеть так:
Таким способом получается легко управлять порядком подключения стилей. Проверим наш таск, запустив:
Собираем картинки
Таск по картинкам будет выглядеть так:
Я использую дефолтные настройки imagemin, за исключением interlaced. Подробнее об API этого плагина можно прочесть тут.
Теперь, если мы положим какую-нибудь картинку в src/img и запустим команду:
то увидим в build нашу оптимизированную картинку. Так же gulp любезно напишет в консоли сколько места он сэкономил для пользователей нашего сайта.
Шрифты
Со шрифтами мне обычно не нужно проводить никаких манипуляций, но что бы не рушить парадигму «Работаем в src/ и собираем в build/» — я просто копирую файлы из src/fonts и вставляю в build/fonts. Вот таск:
Теперь давайте определим таск с именем «build», который будет запускать все что мы с вами тут накодили:
Изменения файлов
Чтобы не лазить все время в консоль давайте попросим gulp каждый раз при изменении какого то файла запускать нужную задачу. Для этого напишет такой таск:
С понимаем не должно возникнуть проблем. Мы просто идем по нашим путям определенным в переменной path, и в функции вызывающейся при изменении файла — просим запустить нужный нам таск. Попробуйте запустить в консоли:
И поменяйте разные файлы. Ну не круто ли?
Веб сервер
Что бы насладиться чудом livereload — нам необходимо создать себе локальный веб-сервер. Для этого напишем следующий простой таск:
Тут даже нечего комментировать. Мы просто запустим livereload сервер с настройками, которые мы определили в объекте config. К тому же gulp вежливо откроет наш проект в браузере, а в консоль напишет ссылки на локальный сервер, и на тунель, который мы можем скинуть заказчику для демонстрации.
Очистка
Если вы добавите какую-нибудь картинку, потом запустите задачу image:build и потом картинку удалите — она останется в папке build. Так что было бы удобно — периодически подчищать ее. Создадим для этого простой таск:
Теперь при запуске команды:
просто будет удаляться папка build.
Финальный аккорд
Последним делом — мы определим дефолтный таск, который будет запускать всю нашу сборку.
Окончательно ваш gulpfile.js будет выглядеть примерно вот так. Теперь выполним в консоли:
И вуаля. Заготовка для вашего проекта готова и ждет вас.
Пара слов в заключение
Эта статья задумывалась как способ еще раз освежить в памяти тонкости сборки frontend проектов, и для легкости передачи этого опыта новым разработчикам. Вам не обязательно использовать на своих проектах именно такой вариант сборки. Есть yeoman.io, на котором вы найдете генераторы почти под любые нужды.
Я написал этот сборщик по трём причинам.
- Мне нравится использовать rigger в своем html коде;
- Почти во всех сборках что я встречал — используется временная папка (обычно .tmp/), для записи промежуточных результатов сборки. Мне не нравится такой подход и я хотел избавиться от временных папок;
- И я хотел что бы все это было у меня из коробки.
Мою рабочую версию сборщика вы можете скачать на моем github. Надеюсь, статья оказалась полезной для вас.
P.S. Обо всех ошибках, недочетах и косяках — пожалуйста пишите в личку.
Источник: https://habrahabr.ru/post/250569/