Позавчера обнаружил новость о том, что команда 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
> Документация к плагину

Спасибо за внимание.
Поделиться с друзьями
-->

Комментарии (7)


  1. MetaDone
    01.07.2017 08:58
    +2

    Полезная фишка, судя по всему вдохновленная Laravel Mix/Laraver Elixir


    1. franzose
      01.07.2017 12:21

      Так и есть, в документации указано влияние)


  1. Quber
    02.07.2017 14:31

    Большое спасибо. Очень интересный материал


  1. borNfree
    04.07.2017 23:35

    Стоит отметить, что релизнута была поддержка TypeScript, что делает этот интсрумент еще более интересным.


    1. shude
      04.07.2017 23:43

      Про Webpack в целом можно много написать. Данный пост относится именно к плагину для Symfony — Webpack Encore. Насколько я знаю TypeScript уже давно прикрутили.


      1. borNfree
        04.07.2017 23:45
        +1

        я именно про Encore и говорю, поддержка TypeScript была вмержена 5 дней назад


        1. shude
          04.07.2017 23:59
          +1

          Точно. Как-то я пропустил. Спасибо, добавлю в пост информацию.