Webpacker — новый стандарт сборки фронтенда для Rails

Alexander Khramov
6 min readJun 27, 2017

--

Обновлено: 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.0
before_install:
- ./install_yarn.sh
install:
- bundle install
- yarn
- bin/webpack

Скрипт install_yarn.sh рядом выглядит так:

#!/usr/bin/env bash
set -ex
curl -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.list
sudo apt-get update && sudo apt-get install yarn

Не забудьте сделать файл исполняемым: chmod +x install_yarn.sh

Итого

Я перевел текущий проект на webpacker, собираюсь использовать webpacker для всех своих новых проектов на Rails и всем советую. Впрочем и Sprockets можно оставить: как fallback и для gem’ов, которые полагаются на Sprockets в своей работе.

--

--