背景
-
Rails 7.0系から7.1系にアップデートするにあたり、すでに開発がストップしている Webpacker を乗り換える必要がありました
-
Webpackerを使ってReactを動かしていたので、それもビルドできるようにする必要がありました。(React内でCSSも使っていました)
-
一方で、React以外のCSSは Sprockets を使って管理していたので、これはそのまま残したい
読んで欲しい人
- 同じような場面に直面した人
- 過去のなーんにもわかってない俺
環境について
- Rails 7.0.8
- Ruby 3.2.2
- Webpacker 5.4.4
- sprockets 4.2.1
- React 16.0.
簡単に用語の整理
Sprockets
- app/assets配下にある、js,css,画像などのアセット群を1つにまとめてくれる。Rails6より前でディフォルトだったぽい。Webpackerとは役割が被っている
webpackerとは
- Rails6.0より標準搭載となったWebpackを使用したjs,css,画像などのアセット群を1つにまとめてくれるモジュールバンドラーです。Rails7.0以降には推奨されなくなり、現在では開発もストップしています。
- Webpackとは?
- 複数のjsファイルを1つにまとめてくれるモジュールバンドラーです。node.jsのモジュールの1つ
- js以外のcssや画像ファイルもまとめてくれるが、ややこしくなるのでこの記事ではjs以外はスルーします。
- node.jsとは
- モジュールバンドラーとは
- 複数のJSファイルの依存関係を勝手に解析して、いい感じに1つのファイルにまとめてくれる機能があるやつ
jsbundling-railsとは
- Railsとjsバンドラーの連携をいい感じにしてくれる「ブリッジツール」的なもの
- ビルドしてくれた後のファイルを適切な場所に置いてくれる
- バンドラーが初期設定を行なってくれる
esbuild
- jsのバンドラー
- jsのバンドルを比較的高速に行ってくれるらしい
jsbundling-railsとesbuild(別のバンドラーでも良いが)の2つが揃って連携して初めて、Webpackerの代わりとして機能します
乗り換え
jsbundlig-rails
- gemfileに
gem 'jsbundling-rails'
を追記
esbuild追加
- esbuildのインストール
$ ./bin/rails javascript:install:esbuild
そうすると下記のファイル生成、変更が行われる
- .gitignoreファイルの更新
- foremanで複数のプロセスを実行するためのProcfile.devファイルの生成
- app/assets/buildsディレクトリ→この時点では.keepファイルだけ
- manifest.jsの更新
- app/javascript/application.jsのエントリーポイントファイル生成
- javascriptのインクルードタグ追記
- bin/devスクリプトファイル生成
- pakage.jsonの更新
生成されたファイルをざっくり説明
- Procfile.devファイル
- Procfile.devに書かれた複数のプロセスを
foreman start
コマンド1つで一度に立ち上げてくれます
- Procfile.devに書かれた複数のプロセスを
# Railsサーバーの立ち上げ
web: bin/rails server
# バンドラーの実行(後述)
js: yarn build --watch
- bin/devスクリプトファイル
-
bin/dev
とターミナルに打ち込むとforeman start
を実行してくれるファイル
-
# bin/dev
#!/usr/bin/env bash
# foremanがインストールされていなければインストール
if ! command -v foreman &> /dev/null
then
echo "Installing foreman..."
gem install foreman
fi
# Procfile.devを元にforemanを起動
foreman start -f Procfile.dev "$@"
-
app/assets/buildsディレクトリ
- esbuildでビルドした後のファイルをここで管理してくれる
- この時点では.keepファイルだけ
-
pakage.jsonのscript
- 下記のようなコマンドが追加されている
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --public-path=/assets"
}
- app/javascript/application.jsのエントリーポイントファイル
- esbuildのエントリーポイント。Webpackerでいうところのapp/javascript/packs/application.jsにあたる
jsの移行
- Sprocketsを利用している
- esbuildでcssファイルもビルドしたい
みたいな場合はapp/javascript/application.jsのエントリーポイントファイル名を、app/javascript/esbuild_application.js(任意の名前)にしてください。詳しくは「詰まった箇所」で後述しますが、既存のSprocketsの生成したcssファイルと名前がかぶるのでSprockets::DoubleLinkErrorsが出てはまります
- ReactをWepackerのエントリーポイントからesbuildのエントリーポイントに移動させます
require("controllers")
require("@rails/ujs").start();
import React from 'react';
import { render } from 'react-dom';
// Reactで使用していたcss 後述
import 'slick-carousel/slick/slick.css';
import "slick-carousel/slick/slick-theme.css";
import 'slick-carousel/slick/slick-theme.css';
// Reactのコンポーネントimport
import Slide from './components/Slide';
import PresentationEditor from './components/PresentationEditor';
import "./controllers"
import Rails from “@rails/ujs”
Rails.starts()
import React from 'react';
import { render } from 'react-dom';
// Reactで使用していたcss
import 'slick-carousel/slick/slick.css';
import "slick-carousel/slick/slick-theme.css";
import 'slick-carousel/slick/slick-theme.css';
// Reactのコンポーネントimport
import Slide from './components/Slide';
import PresentationEditor from './components/PresentationEditor';
app/javascript/packs/application.jsでrequire
を使用している部分はimport
に変更してください
webpackerで使用しているtagを削除する
-
webpackerで使用していた
javascript_pack_tag
などをここでjavascript_include_tag
に差し替えます -
esbuildでcssもビルドしている場合は
stylesheet_link_tag
を利用してください。sprocketsを利用している場合はesbuildで読み込むためのstylesheet_link_tag
も必要になります
<%= stylesheet_link_tag 'esbuild_application' %>
<%= javascript_pack_tag 'application' %>
<%= javascript_include_tag "application", defer: true %>
or
<%= javascript_include_tag "esbuild_application", defer: true %>
Webpacker掃除
- Webpacker関連のファイルやライブラリなどを削除します
- gemfileからの削除
- ./bin/webpack
- ./bin/webpack-dev-server
- ./config/initializers/webpacker.rb
- ./config/webpacker.yml
- ./config/webpack/development.js
- ./config/webpack/environment.js
- ./config/webpack/production.js
- ./config/webpack/test.js
package.josnのscriptを修正
- pakage.jsonのscriptでReactの拡張子を認識させる
- scriptに
--loader:.js=jsx
オプションを入れます。jsxファイルをインポートして、依存関係に含めます。そしていい感じにコンパルしてくれます
- scriptに
"scripts": {
"build": "esbuild app/javascript/*.* --bundle --sourcemap --format=esm --outdir=app/assets/builds --loader:.js=jsx
}
詰まったこと
-
詰まった箇所: Reactで使用しているcssファイル
import 'slick-carousel/slick/slick.css'; import "slick-carousel/slick/slick-theme.css"; import 'slick-carousel/slick/slick-theme.css';
- ReactのSlick内で使用されているCSSの設定方法で詰まった。
- CSSだけSprocketsに移動しようとしたができなかったです。原因はいまだに不明
- cssbundling-railsを利用してcssをビルドしようとしたが、できなかった。原因はすでにesbuildで生成されていることを知らなかったから(esbuild_application.jsからの消し忘れで気づけた)
- Sprocketsのcssファイル名と競合してSprockets::DoubleLinkErrorsエラーになってしまった。原因はどちらも最終的にはapplication.cssファイルとして読み込まれるので同じファイルが2つあると認識されていた。esbuildで生成されたcssファイルについてはesbuild_application.cssとしてファイル名を変更して解決
- ReactのSlick内で使用されているCSSの設定方法で詰まった。
感想
- そもそもRailsのフロント周りは移り変わりが激しく色々な単語が登場するのでとても苦戦しました
- 「Webpacker剥がし」なるものを実際に1人で経験できて前よりもフロントへの解像度が増えてよかったです
参考にした記事