Rails 7で増えるアセット管理の選択肢
Rails 7では、Webpackerが標準でなくなりましたが、これに替わるアセット管理方式の選択肢が増えます。このために、4つの新しいGemライブラリが追加されました。これらが、propshaft、importmap-rails、jsbundling-rails、cssbundling-railsです。PropshaftまたはSprocketsに、他のライブラリをアセット管理方式に合わせて組み合わせていく形になります。図3に、ライブラリの組み合わせとアセット管理方式の違いをまとめておきます。詳細は次回以降で取り上げていきますので、ここでは概略だけを示します。
Webpackerが非標準になったわけ
Rails 7において、Webpackerの使用を前提としなくなったのは、以下の技術的な背景があるとRailsの開発者であるDHH氏のブログ記事では述べています。
(1)ES6が主要Webブラウザでサポートされるようになった
スマートフォン用を含めた主要なWebブラウザがES6をサポートし、ES6で書かれたJavaScriptコードをトランスパイルなしに実行できるようになりました。ES6は、クラス、モジュール、アロー関数などモダンなJavaScriptプログラムに必須とされる構文をサポートしている初めてのバージョンであり、これをトランスパイルなしに実行できることは、BabelないしWebpackの必要性を大きく下げるものになるわけです。
(2)HTTP/2が普及した
HTTP(HyperText Transfer Protocol)の新しいバージョンであるHTTP/2がWebサーバ、Webブラウザともに普及が進んだことで、必ずしもHTTP/1.1にこだわる必要がなくなりました。長らく使われてきたHTTP/1.1には、リソースの要求の都度にコネクションの確立が必要という大きな問題がありました。比較的軽量で、その替わり数が多くなりがちなJavaScriptファイルやCSSファイルといったリソースをWebクライアントが都度要求すればその頻度は膨大となります。これは、負荷の大きなコネクションの確立も相まって、パフォーマンス悪化の原因となってきました。HTTP/2では、単一のコネクションでリソースの要求を続けて行うことができ、この問題を解決したことでモジュールバンドラーの必要性を大きく下げています。また、テキストベースであったHTTP/1.1に対してHTTP/2ではバイナリベースの通信を行うので、リクエストヘッダのエンコードが不要になることなどもパフォーマンス的に有利に働きます。
(3)Import Mapsが一部のWebブラウザだが利用可能になった
さらに、後述するImport Mapsにより、インポートするJavaScriptモジュールをモジュール名だけで記述できるようになったことで、コード上での管理が容易になりました。ES6では、JavaScriptファイルに含まれる変数や関数をエクスポートし、それを別のJavaScriptファイルでインポートして利用するモジュールの仕組みが導入されましたが、この際インポートするモジュールはモジュールの場所を絶対/相対パスで指定する必要があり、それを記述する場所が多くなれば管理が煩雑になるという問題がありました。そこで、Node.jsのようにモジュールを名前だけで参照できれば便利ということで、Import Mapsの仕組みが考えられました。Import Mapsを使うと、モジュールの名称と場所を一元管理でき、モジュールを利用する側はその名前だけを記述すればよくなっています。
新しいアセットパイプラインPropshaft
Propshaftは、Sprocketsに替わるアセットパイプラインのためのライブラリです。Railsアプリケーション作成時に-aオプションを指定することで、デフォルトになっているSprocketsに替わりPropshaftを使用することを指示できます。
PropshaftがSprocketsと大きく異なるのは、トランスパイル、圧縮・連結などの処理を行わないことです。単にダイジェストをアセットのファイル名に付加して公開フォルダにコピーするだけなので、高速に動作します。トランスパイル、圧縮・連結が不要なら、Sprocketsに替わりPropshaftを使用すればよいわけです。
また、asset-path("ファイル名")というCSS関数が用意されており、アセットのプリコンパイル時にurl("/assets/image-….svg")のようなダイジェストを用いたファイル名に変換してくれますので、CSSにおけるアセットの参照も容易となっています。
Import Mapsを自動化するimportmaps-rails
importmaps-railsは、Import Mapsの機能をRails上でサポートするGemライブラリです。Railsアプリケーション作成時にはデフォルトでこのimportmaps-railsがインストールされますので、Sprockets+Import MapsがRails 7のアセット管理におけるデフォルト構成と言えます。
Import Mapsでは、バージョン管理されたファイル名やダイジェストに対応したファイル名を使わずに、それらとは切り離された論理名でWebブラウザから直接JavaScriptモジュールをインポートすることができます。これにより、モジュールをバンドリングしたりする必要性がなくなりました。
Import Mapsを使うHTMLには、実際のファイル名と論理名のマッピングをJSON形式で指定しておきます。JavaScriptのコード中では、この論理名を指定してimport文を記述するだけです。これで、Webブラウザが論理名を実際のファイル名に変換して、モジュールのインポートを行ってくれます。
Railsでは、config/importmap.rbファイルにDSL構文を使ってマッピングの設定を記載しておきます。以下は、importmap-railsによって作成されるconfig/importmap.rbファイルの内容です。
pin "application", preload: true pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true pin_all_from "app/javascript/controllers", under: "controllers"
pinは主に、論理名と実際のファイル名の対応をマッピングに追加します。pin_all_fromは、指定したディレクトリ以下のファイルをマッピングに追加します。ビュー側では、javascript_importmap_tagsヘルパーを使い、必要なタグを生成します。以下は、/app/views/layouts/application.html.erbに記述されているヘルパーの呼び出しです。
<%= javascript_importmap_tags %>
この呼び出しで、必要な<script>タグが自動的に生成されます。なおImport Mapsは、執筆時点でGoogle ChromeやMicrosoft Edgeなどの一部のWebブラウザでしかサポートされていませんが、shim(既存コード修正のためのコード)を使用することで利用可能になります。importmaps-railsでは、自動的にES Module Shimsというモジュールが読み込まれて、Import MapsをサポートしないWebブラウザでもImport Mapsが利用できるようになっています。
バンドラーを使用するjsbundling-railsとcssbundling-rails
jsbundling-railsは、Webpackやesbuild、rollup.jsといったモジュールバンドラーの使用をサポートするGemライブラリです。Railsアプリケーション作成時に-jオプションを指定してバンドラーを選択できます。Webpackerの新規開発が中止されたことで、WebpackをRails 7においても引き続き使いたいといった場合には、このjsbundling-railsを使ったアセットパイプラインの構築がひとつの選択肢となります。
また、jsbundling-railsとは直接の関係はないですが、開発中止となったWebpackerの代替とされているShakapackerをインストールして使うのも、もうひとつの選択肢です。この場合には、Gemfileを編集するなどしてShakapackerのGemライブラリをインストールします。
cssbundling-railsは、BootstrapやDart Sass、PostCSSなどをアプリケーションに組み込む際に必要に応じて使用します。
Railsの環境構築
次回以降、Railsによるクライアントサイド開発について見ていきますが、必要に応じてアプリケーションを作成し、その構成を見ていきます。そのために、読者の手元にもRailsの開発環境を作っておくことをお勧めします。ここでは、macOSを例に開発環境の構築について概要のみ紹介します。
環境構築は、以下の手順で行います。
- Homebrew(macOS用のパッケージマネージャー)がなければインストール
- rbenv(Rubyのインストーラ)をHomebrewでインストール
- Rubyをrbenvでインストール
- Railsをインストール
Homebrewがインストールされていない場合には、まずはこれをインストールしておきます。macOS(またはLinux)用パッケージマネージャーのページにアクセスして表示されるシェルスクリプトを実行すると、それだけでHomebrewがインストールされます。
次に、rbenvをHomebrewでインストールします。rbenvがインストールできたら、rbenvでインストール可能なバージョンを確認し、バージョン3.1.0がリストにあることを確認してインストールを行います。インストールできたら、使用するバージョンを3.1.0に設定します。
% brew install rbenv …略… % rbenv install -l インストールできるバージョンの確認 …略… 3.1.0 …略… % rbenv install 3.1.0 バージョン3.1.0をインストール …略… % rbenv global 3.1.0 バージョン3.1.0をアクティブにする
ログイン時の設定を~/.zshrcファイルに下記の内容で追加します。ファイルがなければ新規に作成して下さい。
export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init - zsh)"
ここで、sourceコマンドで~/.zshrcファイルを読み込むか、あるいはターミナルを開き直して、PATHの設定などを反映させます。最後にRailsをインストールします。
% gem install rails …略… 35 gems installed
ここで、ターミナルを開き直すと、インストールしたRailsのコマンドが有効になります。これで、Railsを使う準備は整いました。
まとめ
今回は、Ruby on Rails 7について、そのアセット管理を中心に取り上げました。Railsにおけるクライアントサイド開発サポートの変化を振り返りながら、概略をお伝えできたのではないかと思います。次回は、Rails 7で大きく刷新されたアセット管理について掘り下げて紹介したいと思います。