前置き
上二つの続き。
Rails7 -> Ruby3.1時たら今度はhotwireとimportmapじゃろうということで、Rails6->Rails7にしたアプリをwebpackerからhotwireに移行する。
hotwireって?
basecampが作った新しい手法。
Javascriptの利用を抑え、Jsonを送信せず、htmlのやりとりに抑える。turbolinksと呼ばれていた機能はTurboという形でhotwireに組み込まれた。これにStimulusが追加されてhotwireって呼ばれている。
元はhotwired_railsとかいうGemがあって、rails6で導入するためにはrails newするときに–skip-javascriptが必要だったんだけど、Rails7からはrails newすると自動的にhotwireのセットが入る。
Stimulusって?
HTMLのレンダリングをしない小さなJavaScriptフレームワーク。READMEにあるけど本当にhtmlの描画をしないので、Turboと組み合わせて使うことで力を発揮するように作られている。
importmapって??
import句でモジュールを取得する際の取得先URLを制御する仕組み。import句で読み込み先をコントロールする。これの導入でWebpackを使わずRailsでJavaScriptを扱うことが可能になる。
対象アプリ
僕が今作ってるアプリはAPIの管理画面で、そちらはRails WayなCRUDの画面がいっぱいある。bootstrapでデザインしてて、モーダルや画像表示などは完全にbootstrapに依存している。bootstrapのインストールについてはこっちの記事で書いた。
これをhotwire / importmapに移行して、bootstrap5のデザインが維持されているところまでがゴール。
Rails6->Rails7にしたあとのGemfile
3.1対応まだなのでRails7.0。このあと7.0.1にする記事を書く予定。
gem 'rails', '7.0.0'
gem 'sass-rails', '>= 6'
## TurbolinksはもうActiveではない。HotWiredに移行すること。
# https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5' <<<<< 今回はここを変えていく
gem 'webpacker'
gem 'bootsnap', '>= 1.4.2', require: false
こうする
gem 'rails', '7.0.0'
gem 'sass-rails', '>= 6'
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
gem "importmap-rails"
# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
gem "turbo-rails"
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
gem "stimulus-rails"
gem 'bootsnap', '>= 1.4.2', require: false
インストールする。
$ bundle install
turboを有効にする
不要なファイルを消す。Rails7からはnodeが不要になるので、js関連のファイルは不要。
$ rm -rf package.json yarn.lock node_modules postcss.config.js babel.config.js
app/javascriptの場所を変える。Webpackも同時に不要なのでpack以下ではなくjavascript配下に置く。
$ mv app/javascript/pack/application.js app/javascript/application.js
$ mkdir app/javascript/controller
$ rm -rf pack
application.jsを編集する。
import "@hotwired/turbo-rails"
import "controllers"
require("@rails/activestorage").start()
require("channels")
import "bootstrap"
import "./stylesheets/application"
const images = require.context('./images', true)
const imagePath = (name) => images(name, true)
そしたらimportmapの設定ファイルを作成する。turboのインストールに必要。
touch config/importmap.rb
最後にコマンドをうつ。これでturboがpinづけされて利用が可能になる。
$ rails turbo:install
Import Turbo
append app/javascript/application.js
Pin Turbo
append config/importmap.rb
このImport何とかとPinについてはこのあとのimportmapで説明する。
Importmapを有効にしてbootstrapを呼び出す
設定ファイルを見てみる。
# config/importmap.rb
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
turboはhotwiredのjsにpinづけされてる。
こうして名前と呼び出し元をマッピングすることでapplication.js内でimportするだけで呼び出しが可能になる。
$ rails importmap:install
Default application.html.erb is missing!
Add <%= javascript_importmap_tags %> within the <head> tag in your custom layout.
Create application.js module as entrypoint
conflict app/javascript/application.js
Overwrite /app/app/javascript/application.js? (enter "h" for help) [Ynaqdhm] Y
force app/javascript/application.js
Use vendor/javascript for downloaded pins
create vendor/javascript
create vendor/javascript/.keep
Ensure JavaScript files are in the Sprocket manifest
append app/assets/config/manifest.js
Configure importmap paths in config/importmap.rb
conflict config/importmap.rb
Overwrite /app/config/importmap.rb? (enter "h" for help) [Ynaqdhm] Y
force config/importmap.rb
Copying binstub
create bin/importmap
必要なものをimportする。app/javascript/application.js
を開いて、以下の形に書き換える。上でpinつけしているので、これで外部のCDN が呼び出される。
import "@hotwired/turbo-rails"
import "bootstrap"
ただこれだとJavaScriptは呼ばれてもCSSは呼ばれない。
CSSは別途インストールする。
Gemに以下を追加。
gem "cssbundling-rails"
gem "foreman"
Rails7はCSSをいくつか選んでインストールできるので、好きなの選ぶんだけど、今回はbootstrapを選ぶ。
# コマンドサンプル
./bin/rails css:install:[tailwind|bootstrap|bulma|postcss|sass]
# 今回はこう
./bin/rails css:install:bootstrap
package.jsonに以下を追加。こうしないとbootstrapがビルドされない。これnode_modulesを使わない方法があるのかもしれないので、それはまた別途。
"scripts": {
"build:css": "sass ./app/assets/stylesheets/application.bootstrap.scss ./app/assets/builds/application.css --no-source-map --load-path=node_modules"
}
あとはいつも通りdocker-compose upで起動すれば画面にbootstrapがあたっている。
まとめ
なんだかんだいって最後にyarn installが走っているので、nodeとyarnを使わない方式にできるならやりたいと思った。
少なくても今回でWebpackerは脱出できた。
次はbootstrapをtailwindcssにリニューアルしてみようと思う。