Позавчера обнаружил новость о том, что команда Symfony выпустила плагин Webpack Encore для интеграции замечательного инструмента Webpack в ваше приложение. Если вы не знакомы с Webpack, то я настоятельно рекомендую ознакомиться с ним, так как он возможно решит множество вопросов связанных с управлением ресурсами в вашем проекте. В любом случае даже если вы не собираетесь его использовать, знать о том что он существует будет крайне полезным. Очень хорошо этот инструмент описан тут.
Вступление
В свое время я опробовал множество разных подходов и инструментов для управления ресурсами. Как правило для каждого проекта приходилось выбирать, в зависимости от сложности структуры, разные решения. Но какое решение я бы не выбрал у меня всегда оставалось ощущение некоторой неудовлетворенности. Всегда оставался либо какой-то костыль, либо приходилось подстраиваться под решение.
Два дня назад я попробовал плагин для Symfony проекта Webpack Encore. Всего два дня и возможно через некоторое время я изменю свое мнение по поводу этого плагина, но сейчас находясь под впечатлением я хочу показать вам те возможности, которые он предлагает.
Необходимые инструменты
Вам потребуется какой-либо тестовый проект на Symfony >= 3.3, NodeJS и менеджер пакетов Yarn. Вы можете использовать Npm, но в данном посте примеры будут с использованием Yarn.
Для теста мы будем подключать к проекту FontAwesome, Jquery, Bootstrap и какие-то свои выдуманные ресурсы.
Установка
Сначала установим плагин Webpack Encore. В корне приложения:
yarn add @symfony/webpack-encore --dev
Мы будем использовать SASS, поэтому добавим пару пакетов:
yarn add sass-loader node-sass --dev
Не забудьте добавить в .gitignore каталог node_modules.
Подготовка ресурсов
Я создам в корне приложения каталог assets/ куда положу все необходимые ресурсы. В результате мой каталог будет выглядеть так:
+-assets/
---+ dist/
------+ fontawesome/
------+ jquery/
------+ bootstrap/
Дополнительно я создам в корне assets/ файл app.scss, который будет главным файлом ресурсов. Вовсе не обязательно иметь каталог dist с библиотеками, их можно установить с помощью Yarn. Я выбрал такой путь для большей наглядности.
Теперь необходимо создать инструкции для плагина. Для этого в корне приложения создадим файл webpack.config.js со следующим содержимым:
/* подключим плагин */
var Encore = require('@symfony/webpack-encore');
Encore
/* Установим путь куда будет осуществляться сборка */
.setOutputPath('web/build/')
/* Укажем web путь до каталога web/build */
.setPublicPath('/build')
/* Каждый раз перед сборкой будем очищать каталог /build */
.cleanupOutputBeforeBuild()
/* Добавим наш главный файл ресурсов в сборку */
.addStyleEntry('styles', './assets/app.scss')
/* Включим поддержку sass/scss файлов */
.enableSassLoader()
/* В режиме разработки будем генерировать карту ресурсов */
.enableSourceMaps(!Encore.isProduction());
/* Экспортируем финальную конфигурацию */
module.exports = Encore.getWebpackConfig();
Теперь можно заняться ресурсами. Отредактируем наш файл app.scss:
@import "dist/fontawesome/css/font-awesome";
@import "dist/bootstrap/css/bootstrap";
/* Тут можно определить свои стили или подключить собственные библиотеки */
Запускаем билд. В корне приложения:
./node_modules/.bin/encore dev
Если все прошло гладко, вы увидите в каталоге web/build файл styles.css, а так же папку fonts, куда скопированы все шрифты font-awesome на которые ссылается font-awesome.css. Если вы прописали какие-то свои стили, которые используют изображения, то эти изображения так же подтянутся в папку web/build/images. В результирующих файлах стилей все пути соответственно будут переписаны.
Благодаря сгенерированным картам ресурсов, мы можем комфортно использовать отладчик в браузере. Помимо стилей, в каталоге web/builds появится файл manifest.json, о нем чуть позже.
Сейчас необходимо подключить JavaScript, который нам необходим. Для этого добавим в каталог assets/ файл app.js со следующим содержимым:
var $ = require('./dist/jquery/jquery-3.2.1');
require('./dist/bootstrap/js/bootstrap');
Теперь отредактируем немного наш файл webpack.config.js:
/* подключим плагин */
var Encore = require('@symfony/webpack-encore');
Encore
/* Установим путь куда будет осуществляться сборка */
.setOutputPath('web/build/')
/* Укажем web путь до каталога web/build */
.setPublicPath('/build')
/* Каждый раз перед сборкой будем очищать каталог /build */
.cleanupOutputBeforeBuild()
/* --- Добавим основной JavaScript в сборку --- */
.addEntry('scripts', './assets/app.js')
/* Добавим наш главный файл ресурсов в сборку */
.addStyleEntry('styles', './assets/app.scss')
/* Включим поддержку sass/scss файлов */
.enableSassLoader()
/* В режиме разработки будем генерировать карту ресурсов */
.enableSourceMaps(!Encore.isProduction());
/* Экспортируем финальную конфигурацию */
module.exports = Encore.getWebpackConfig();
Теперь перезапустим сборку:
./node_modules/.bin/encore dev
Если все прошло гладко, вы получите файл scripts.js в каталоге web/builds.
У вас может возникнуть проблема со скриптами, которые ожидают, что JQuery будет доступен глобально. Когда вы делаете var $ = require(some.js), то просто подключаете скрипт в текущий контекст, а не глобально. Поэтому скрипты, которые вы определяете в шаблонах, а так же некоторые другие библиотеки, ожидающие глобального JQuery работать не будут.
Есть несколько вариантов решения проблемы. Для всех пакетов, которые вы подключаете через require, обеспечить доступ к $ или JQuery, можно добавив в файл webpack.config.js такую инструкцию:
/* подключим плагин */
var Encore = require('@symfony/webpack-encore');
Encore
/* Установим путь куда будет осуществляться сборка */
.setOutputPath('web/build/')
/* ... */
.autoProvidejQuery()
/* В режиме разработки будем генерировать карту ресурсов */
.enableSourceMaps(!Encore.isProduction());
/* Экспортируем финальную конфигурацию */
module.exports = Encore.getWebpackConfig();
Теперь все в порядке, однако скрипты, которые вы определяете в шаблонах по прежнему не будут видеть JQuery. В таком случае вероятно вам понадобится вручную добавить библиотеку в глобальную область видимости. Для этого отредактируем файл app.js:
var $ = require('./dist/jquery/jquery-3.2.1');
global.$ = global.jQuery = $;
require('./dist/bootstrap/js/bootstrap');
Еще раз запустим сборку и теперь у нас все очень компактно упаковано, разложено по каталогам, мы имеем один файл стилей и один файл скриптов. Все зависимости (шрифты, картинки) скопированы в соответствующие места.
Включение версионирования:
/* webpack.config.js */
// ...
.enableVersioning()
// ...
Если вы включили данную функцию, то вам потребуется внести некоторые настройки в конфигурацию вашего Symfony проекта:
# app/config/config.yml
framework:
# ...
assets:
# Функционал доступен начиная с Symfony 3.3
json_manifest_path: '%kernel.project_dir%/web/build/manifest.json'
Файл manifest.json, хранит карту соответствия файлов ресурсов их версионным аналогам. Теперь подключая в вашем Twig шаблоне стиль:
<link href="{{ asset('build/styles.css') }}" rel="stylesheet" />
на самом деле будет подключен файл вида: build/styles.c1a32e.css
Подключение стилей через JavaScript:
Вы вполне можете себе позволить импортировать файлы стилей через JavaScript. На примере нашего проекта можно было бы сделать так в app.js:
require('./app.scss');
var $ = require('./dist/jquery/jquery-3.2.1');
global.$ = global.jQuery = $;
require('./dist/bootstrap/js/bootstrap');
В этом случае, в webpack.config.js не нужно добавлять addStyleEntry. После сборки автоматически будет создан js файл и одноименный css файл со всеми запрошенными стилями в данном скрипте.
Деплой
Достаточно холиварная тема, но я все-таки затрону ее. Если вы предпочитаете производить сборку на стороне сервера, тогда вам стоит добавить в .gitignore каталог web/build и на продакшн сервере выполнять:
./node_modules/.bin/encore production
В режиме production ваши скрипты и стили будут дополнительно минифицированы.
Если же вам критично время развертки проекта на продакшене, то вы можете производить сборку ресурсов и коммитить уже готовые файлы в ваш репозиторий.
Заключение
Данный материал не претендует на использование в реальных условиях. В каждой конкретной ситуации будут свои подходы и настройки. Я лишь хотел показать еще один вариант управления ресурсами в проекте. Те кто знаком с webpack, вероятно найдут для себя полезной информацию о том, что появился такой плагин от разработчиков Symfony. Кто не знаком с webpack возможно заинтересуются этим инструментом.
Многие вещи хотелось бы описать более подробно, но сколько я ни пытался, все равно получался слишком раздутый пост. Я готов доработать материал, если будет адекватная критика. Ниже привожу ссылки, где можно более глубоко ознакомиться с данными инструментами:
Официальный сайт webpack: https://webpack.js.org/
Документация к плагину: http://symfony.com/doc/current/frontend.html
Спасибо за внимание.
Автор: Владимир Т