Railsにどのようにフロントエンドを連携させるかの技術検証っぽいこと(Rails7 + Webpack + Vue.js3)

この記事は、自作サービスRuumarkerのリリースに先立って、2022年2-3月にメモしたものを記事にしたもの。一つのリリース記事では収めると長すぎるので、フロントエンドの技術検証っぽい部分だけ切り出した。

場当たり的に行ったことの羅列にはなってしまうけど、これからWebサービスRails7 + Webpack + Vueで作る人や、jsbundling-railsを使おうとしている人あたりが参考になるかもしれないと思い、とりあえず文字起こししてみる。

最終的な構成

最終的に今回開発したWEBサービスは技術構成は以下のようになった。

自作サービス構成図

  • Railsのバージョン → 7
  • Vue.jsのバージョン → 3

情報収集:フロントエンドの選択肢

Rails7でサーバーサイドを作ってからフロントエンドのことを考え始めた

自作サービス開発のプロセスは、

仕様決定 → 画面設計 → 技術検証 → サーバーサイド開発 → フロントエンド開発

が基本路線ではあるが、事前のフロントエンドの技術検証がしんどくて手が止まりそうだったので、とりあえずフロントエンドのことは考えずに先にサーバーサイドの開発をしてしまった。

そして、いざフロントエンドを実装、という段になって、はじめて本気で技術調査に乗り出すことになった。

Stimulusって? Hotwire?

フロントエンドの開発に入ってから改めてGemを眺めると、Stimulusとかいう見慣れないライブラリがあらかじめ入っていた。

## Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
gem "stimulus-rails"

Hotwireって何?

これらはRails7のデフォルトのnewを実行した結果、インストールされていたものらしい。別に使わないでも他に方法があるらしい。

軽く調べてみただけでもフロントエンドの選択肢がわりと沢山ありそう。わけがわからなくなってきたので、次のように整理してみた。

あり得そうな選択肢

  1. Rails7 + バニラJS
  2. Rails7 + Webpack + Vue.js (jsbundling-rails)
  3. Rails6 + Webpacker + Vue.js
  4. Rails7 + Hotwire + Stimulus

1. Rails7 + バニラJS

フレームワークを使わずに書くJavaScriptのことをバニラJSと呼ぶらしい。

『流麗なJavaScript』という誤植だらけだけど原本がロングセラーな本がある。これの輪読会に参加していて、JavaScriptを基本的なところから学び直しており、素のJSを使いたい思いが強かったので、まずこれができないか考えた。

しかし、この場合、フレームワークを特に使う必要がない分、ビルドをどうしていいのか分からなかった。Rails+Webpacker+Vue.jsなら似たようなサービスが多いので、見様見真似で構築できそうだけど、Rails+バニラJSという構成だと見習うモデルが見当たらない。

2. Rails7 + Webpack + Vue.js (jsbundling-rails)

こちらは伝統的なWebpackを使い続けたい人のための、Rails7が用意した解答の一つみたいなやつ。

Vue.jsは少し書いたことがあるので採用したくもあるけど、じゃあどうやってビルドするの?と考えると、ドキュメントが充実しているもののひとつとしてWebpackがある。

Nodeレスに移行するまでのつなぎとしての "bundling-rails" gem

jsbundling-railsというライブラリによって、Webpackで生成したアセットをRailsに取り込むことができるようになっているとのこと。

詳しくは分からないけど、Rails7でサポートされているのが心強い。これができれば、Node.jsのほうでも経験値が溜まってよさそう。

3. Rails6 + Webpacker + Vue.js

こちらは最も保守的な構成。

この構成のアプリは主流なので手本となるコードやドキュメントが豊富。安定した環境構築が期待できる。

しかし、核となるRubyライブラリのWebpackerがRails7からサポートされなくなり、Webpacker自体の開発も終了することが明らかになっているので、リリース後のどこかのタイミングで、サービス構成を大幅に見直す必要が出てくるのが難点。

さよならWebpacker

ちなみにWebpackはNode.jsのライブラリ。WebpackerはWebpackをRubyでラップしたRails用のライブラリで別物。

この場合は、Rails6へのダウングレードをする必要があるのでRails7の将来的学習コストを負債として積み残すことになる。

4. Rails7 + Hotwire + Stimulus

Rails7でデフォルトでインストールされるフレームワーク。ビルドがほぼすでに自動で終わっているので、環境構築の学習コストが低い。

とはいえ、Stimulusがそれ特有の書き方をするので、学習コストはある。しかも、まだあまり広まってないので、果たしてそのスキルが今後活きるのかどうかが疑問。

Rails 7.0正式リリース、Node.js不要のフロントエンド開発環境がデフォルトに

デモ:それぞれ実際に動かす

やはり触って動かさないとストレスが溜まるので、とりあえず空のプロジェクトを作って動かしてみる。

Rails new でいろいろ試す

Rails 7 をちょこっと試す(さらば、Webpacker 編) - Qiita

この記事に沿って実際にデモアプリを作って、Rails newして、ビルドでの差分をgit diffを使って確認してみることにした。細かい作業の手順は以下のIssueに時系列で記録している。

https://github.com/kasai441/ruumarker/issues/82

rails newは、"."を指定すると現在のディレクトリに作成してくれるので、app、bin、configあたりを消して再実行すると何度も同じディレクトリでnewすることができ、それをcommitに記録して差分を確認できるようにしている。各コミットは以下の通り。

いろいろなRails newのコミット

https://github.com/kasai441/demo7/commits/main

オプションなしで 'rails new . --database postgresql'

これは、Rails7のデフォルト設定。先述の「4. Rails7 + Hotwire + Stimulus」の構成であり、Stimulusが作成される。

'rails new . --database postgresql -j esbuild'

これは、先述の「2. Rails7 + Webpack + Vue.js (jsbundling-rails) 」の、Webpackの代わりにesbuildを使うパターン。esbuildのほうが速くオススメらしいので試してみた。

esbuildというnpmがpackage.jsonに追記されている模様。 このままだとbin/devが動かないが、手動でpackage.jsonにscriptsを追加したところ、bin/devが動くようになった。

{
  "scripts": { "build": "esbuild app/javascript/*.* --bundle --sourcemap --outdir=app/assets/builds" },
  ...
}

しかしながら、esbuildではVue.jsのプラグインができないようだったので、Vue.jsを入れるならば次のWebpackがやはり本命となってくる。

'rails new . --database postgresql -j webpack --skip-sprockets'

これは、先述の「2. Rails7 + Webpack + Vue.js (jsbundling-rails) 」の構成。 --skip-sprockets をしてもsprocketsをインストールする。webpackで圧縮したアセットはRailsのSprocketを使ってコンパイル(?)されるのでSprocketは必須だが、この技術検証の時点ではよく分かっていなかった。

情報収集・デモ:Vue3を動かす

Webpackの採用が濃厚に

ここまできて、Rails7 + Webpack + Vue3 という選択肢が濃厚に。採用理由はやや混雑した思考で、

  • 新しいRails7が動かしたい
  • 昔からあるWebpackを知りたい
  • Vueができるからやっとく

みたいな感じだった。一度触ったことがあるものをもう少し詰めてみたくなる気持ちと、新しいものを知りたいという気持ちが混在している。

Vueがうまくビルドできたら、もうそれを採用ということで、Vueの構築をしてみる。

Rails7 + Webpack + Vue3の調査と構築

まずは、webpack + vue に絞ってドキュメントを漁る。記事のリンクは以下のIssueに記録されている。

webpack + Vue で動作確認 · Issue #83 · kasai441/ruumarker

ここで改めて知ったこととして、Webpackはあまり新しいものではないらしく、2、3年前の記事がたくさん出ていることがわかる。esbuildが速くてよいがvue.jsのプラグインが使えないからダメっぽいことも知る。

jsbundling-railsのビルドはwikiに詳しかった。

jsbundling-rails/switch_from_webpacker.md at main · rails/jsbundling-rails

日本語はこちら

次の記事は、RailsとVue.jsの構成でビルドしている模範となった。

スクラッチからWebpack+Babel+Vue 3を使う - Qiita

Vue3の公式インストールガイドも確認のために参照した。

Vueが動いた時、はじめて安心した。けっこう時間をかけて調査をしたので動かなかったらキツイところだった。この瞬間に、もうこのサービス構成でいくことで決定していたと思う。

実装:途中まで作っていた自作サービスに導入

https://github.com/kasai441/ruumarker/issues/87

こちらのIssueでは、Rails7でRspecやCIまで導入済みの状態から、jsbundling-railsを利用してWebpackとBabelとVueを導入する流れが記録されている。

Webpackはすぐ動くようになった。ところが、Rspecで全落ちしてしまう。importmapがうまく動いていないようなので、迷った末、削除することに。importmapはwebpackと役割が一緒なのか、rails new -j webpackでは生成されていないことから、捨ててよいと判断した。理由はよくわからないが、これでテストが落ちなくなった。

StimulusやTurboを動かすためにはyarnから@hotwired/stimulusや@hotwired/turbo-railsをインストールする必要があるっぽい。Sitimulusはいらないような気がしたが、この時点では残した。

Herokuへのデプロイは全くつまずかなかった。デプロイできて、これまた一安心。開発環境→テスト→本番と、つまずきポイントがいっぱいあったので、苦しかった。

技術検証っぽいことができたような

これまでは、誰かのサービス構成を猿真似しているばかりだった。今回もだいぶ真似はしたが、少し選択的なことを考えたので、らしくなってきたかもしれない。