Webpacker — новый стандарт сборки фронтенда для Rails
Обновлено: webpacker активно развивается и с момента выхода версии 2.0, актуальной во время написания статьи, претерпел ряд изменений, ломающих обратную совместимость. Общая идея осталась той же, что описана в этой статье, но детали реализации меняются. Например, на момент выхода статьи был прямой доступ к конфигурации webpack’а: все файлы конфигурации находились в директории `config/webpack`. С тех пор файлы со стандартной конфигурацией были вынесены в отдельный npm пакет и конфигурирование теперь производится с помощью своеобразного API.
Sprockets, тлен, боль
Начну с того, что Sprockets, он же Rails Asset Pipeline, устарел и довольно давно.
Фронтенд развивается семимильными шагами, а Sprockets за ним не поспевает и не особо стремится. В итоге проблем накопилось немало, среди них:
- ограниченная поддержка новых фронтенд инструментов, у вас будут проблемы если вы захотите использовать какой-нибудь PostCSS плагин или гораздо более быстрый SASS компилятор libsass вместо написанного на Ruby.
- отсутствие поддержки транспайлинга ES2015
- отсутствие поддержки модульности javascript’а
- отсутствие поддержки технологии sourcemaps, которая существует уже много лет
Гибкость конфигурации тоже оставляет желать лучшего. Знаете что сделает Sprockets если явно отключить минификацию и прогнать через него уже минифицированный js-скрипт? Sprockets деминифицирует код и вставит везде свои комментарии, пытающиеся подражать sourcemaps. Я так и не нашел как это отключить.
Sprockets слабо развивается и поддерживается. Версию 4, которая должна была решить часть проблем, обещали выпустить еще с релизом Rails 5, но этого не произошло до сих пор, можно посмотреть на релизы.
При этом Sprockets является rails-специфичным инструментом, тогда как frontend развивается сам по себе и сообщество предпочитает использовать и развивать универсальные инструменты, не учитывающие какой-то определенной специфики. Так, в мире фронтенда для целей сборки с помощью node.js были созданы свои простые task runner’ы (например, довольно популярный gulp) и более умные module bundler’ы (webpack, который помимо прочего умеет разрешать зависимости между разными js-модулями, соединять все в правильном порядке и выкидывать лишнее).
Отдельно стоит сказать про пакетные менеджеры в контексте фронтенда. Если вы используете Sprockets, скорее всего вы используете gem’ы для подключения каких-либо библиотек, связанных с фронтендом. Потому что это общепринятый способ. Проблема в том, что авторы фронтенд библиотек не заботятся о том, чтобы делать и поддерживать gem’ы, они используют репозитории npm и может быть bower. И вам приходится подключать созданный сторонним разработчиком gem, который неизвестно как будет поддерживаться, или же пользоваться костылями вроде rails-assets.org.
В общем всё это давно мне надоело и не только мне. Недовольство выражалось в своих собственных попытках приклеить изолентой gulp и webpack к rails, попытках успешных, но требующих времени и нервов. Однако теперь это можно оставить в прошлом.
Webpacker, счастье, процветание
Webpacker — это gem, предоставляющий гладкую и стандартную интеграцию Rails со сборщиком webpack и пакетным менеджером yarn (это такой модернизированный npm). В комплекте также идут опциональные интеграции с популярными фреймворками/библиотеками react, angular, vue. Актуальная версия Webpacker’а на момент написания статьи — 2.0
Требования такие:
Если у вас Rails 5.1+ вы можете воспользоваться опцией --webpack
, потому что с этой версией webpacker поставляется «из коробки». Например так:
rails new webpacker-example-app — webpack// or this to setup react or angular or vuerails new webpacker-example-app — webpack=reactrails new webpacker-example-app — webpack=angularrails new webpacker-example-app — webpack=vue
В случае Rails 4.2+ можно установить webpacker добавив его в ваш Gemfile и выполнив следующую команду после установки гема:
rails webpacker:installrails webpacker:install:[react, angular or vue] (optional)
Эта команда проверит соответствие системы требованиям и позаботится о создании:
- скриптов запуска webpack и webpack-dev-server, в директории bin
- директории
app/javascript
- директории
config/webpack
иwebpacker.yml
- списка npm пакетов
package.json
(аналог Gemfile) с необходимым наполнением и запустит установку пакетов с помощью yarn, что создаст директориюnode_modules
иyarn.lock
Набор модулей по умолчанию содержит Babel, что позволяет вам использовать новейший синтаксис Javascript.
Настройка
Webpacker предлагает следовать определенным соглашениям, однако всё можно довольно гибко сконфигурировать, загляните в config/webpack
и config/webpaсker.yml
. По порядку:
Конфигурация Webpack
Для каждой среды запуска Rails в config/webpack
есть соответствующий файл конфигурации, общая для всех сред часть конфигурации shared.js
, а также файл configuration.js
, ответственный за обработку настроек из config/webpacker.yml
.
Конфигурация production.js
по умолчанию предписывает webpack’у собрать все модули в единые «бандлы», минифицировать их, добавить к названиям fingerprint’ы, сгенерировать sourcemap’ы и даже сжатые gzip-версии вдогонку. То есть даже больше, чем умеет Sprockets, и при этом все можно конфигурировать как угодно: добавлять и удалять шаги из сборки или изменять их. Например, вам не нужны gzip-версии — нет проблем, просто уберите их из конфига.
В директории config/webpack/loaders
хранятся loader’ы, которые отвечают за обработку и трансформацию файлов по определенным правилам. Это аналог task’ов из других инструментов сборки. Обработчик babel.js
отвечает за .js
и .jsx
файлы и предписывает при помощи транспайлера babel превратить код, написанный с синтаксисом ES2015, в ES5 код. Обработчик coffee.js
отвечает за .coffee
файлы и тоже превращает код в ES5. Опять же, всё можно настраивать, можно добавлять или удалять loader’ы.
Обработка стилей
Внедрение webpacker’а изначально предполагалось именно для решения проблем в работе с javascript, не для замены Sprockets. Предполагалось, что Sprockets продолжит помогать работать со стилями и прочими asset’ами. Однако, код стилей, точно так же, как и javascript, может быть обработан loader’ом, соответствующим вашему любимому препроцессору. Webpacker по-умолчанию генерирует файл config/webpack/loaders/sass.js
, содержащий кусочек конфига для обработки стилей, написанных с использованием sass/scss. Это сделано в первую очередь для облегчения работы со стилями в приложениях, сделанных при помощи компонентного подхода и использующих react или подобные инструменты, которые позволяют подключать стили напрямую в компоненты. Но не обязательно делать именно так, и я рекомендую использовать именно webpack для сборки стилей.
Установка Webpacker также интегрирует PostCSS в сборку, что позволяет добавить немало гибкости и возможностей в управление стилями. Настройка PostCSS осуществляется посредством файла .postcssrc.yml
в корне вашего проекта. Там перечислены подключаемые PostCSS плагины:
- precss, ценность которого сомнительна, когда есть scss
- autoprefixer, отличный инструмент, с которым вы можете забыть о браузерных vendor префиксах
Чего «из коробки» не будет, так это возможности удобно включать asset’ы в css код. Имею в виду аналог asset-url, asset-data-uri из Asset Pipeline. Но для этого есть решение — плагин postcss-assets. Чтобы установить его — выполним yarn add postcss-assets -D
. Это добавит пакет в список package.json и установит его. Далее добавим изменения в .postcssrc.yml
, моя настройка выглядит так:
plugins:
postcss-smart-import: {}
precss: {}
autoprefixer: {}
postcss-assets: {
loadPaths: ['images', '../app/assets_src/images/'],
basePath: 'public',
cachebuster: true,
}
Ваши loadPaths
наверняка будут слегка отличаться. Рекомендую посмотреть readme и поэкспериментировать с настройкой.
В результате вы сможете использовать функции resolve
и inline
в вашем css коде.
Структура директорий
Webpacker создает новую директорию app/javascript
, в ней предлагается хранить весь javascript код и «точки входа» для webpack’а (так называемые packs). Соглашение заключается в том, что точки входа помещаются в директорию app/javascript/packs
, а модули с остальным кодом размещаются рядом в app/javascript
.
Стили по умолчанию по определенным причинам предлагается размещать в той же директории app/javascript
. Хорошо, что это легко настроить в config/webpack/shared.js
. Нас интересует параметр resolve.modules
, который является массивом путей, по которым webpack будет искать файлы. Первой строкой идет resolve(settings.source_path)
. source_path
тут берется из config/webpacker.yml
. Для своего проекта я установил source_path: app/assets_src
, и resolve.modules
выглядит так:
modules: [
resolve(`${settings.source_path}/javascripts`),
resolve(`${settings.source_path}/styles`),
'node_modules'
]
Запуск
Webpacker добавляет два скрипта для запуска сборки:
bin/webpack
для единоразового запуска сборкиbin/webpack-dev-server
для запуска сборки с отслеживанием изменения файлов и пересборкой при изменении. Во время разработки нужно запускать именно это.
Рекомендую ознакомиться с содержимым этих файлов.
На заметку — чтобы получить сборку в production режиме, с целью тестирования например, можно передать параметр --config
с полным путем до соответствующего конфига.
Если вы или ваш коллега добавляли модули в package.json, то их нужно будет установить, иначе webpack при запуске выдаст ошибку о недостающем модуле. В таком случае просто выполните команду yarn
в директории проекта, это как bundle install
, только для npm пакетов.
Деплой
Webpacker вешает новую задачу webpacker:compile
на assets:precompile
, которая выполняется каждый раз, когда запускается assets:precompile
. Если вы совсем отказались от Sprockets, то можно вручную запускатьbundle exec rails webpacker:compile
во время деплоя. И не забывайте про необходимость установки новых фронтенд модулей с помощью команды yarn
.
Подключение
Очень похоже на asset pipeline. Используйте новые хелперы javascript_pack_tag
и stylesheet_pack_tag
. Они добавят нужные HTML тэги, ссылающиеся на скомпилированные pack'и.
Настройка для Continuous Integration
Если вы используете CI систему, то разумеется вам будет нужно ее донастроить: добавить node.js и yarn.
Мы в Evrone для всех проектов успешно используем Vexor.io. Настройка для Vexor с сервером на ubuntu будет выглядеть следующим образом.
Файл vexor.yml в корне проекта будет содержать:
rvm:
- 2.3
node_js:
- 6.11.0before_install:
- ./install_yarn.shinstall:
- bundle install
- yarn
- bin/webpack
Скрипт install_yarn.sh рядом выглядит так:
#!/usr/bin/env bash
set -excurl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.listsudo apt-get update && sudo apt-get install yarn
Не забудьте сделать файл исполняемым: chmod +x install_yarn.sh
Итого
Я перевел текущий проект на webpacker, собираюсь использовать webpacker для всех своих новых проектов на Rails и всем советую. Впрочем и Sprockets можно оставить: как fallback и для gem’ов, которые полагаются на Sprockets в своей работе.