アプリ内でimportmap-railsとPropshaftが果たす役割を見てみる
ここまでReactアプリを作成してきましたが、もう少しアプリケーションの中のしくみを掘り下げてみましょう。
importmap-railsの役割
bin/importmapコマンドではモジュールをピン留めし、結果はconfig/importmap.rbに設定されますが、最終的にはHTMLの中にJSON形式でImport Mapsのインポート定義が出力されます。この様子は、bin/importmapコマンドにjsonオプションを付けて実行することで確認できます。
% bin/importmap json { "imports": { "application": "/assets/application-03d94….js", (1) "react": "https://ga.jspm.io/npm:react@18.0.0/index.js", "react-dom/client": "https://ga.jspm.io/npm:react-dom@18.1.0/client.js", "process": "https://ga.jspm.io/npm:@jspm/core@2.0.0-beta.24/nodelibs/browser/process-production.js", "scheduler": "https://ga.jspm.io/npm:scheduler@0.21.0/index.js", "components/react_hello": "/assets/components/react_hello-902bd….js" (2) } }
なお、(1)(2)ではモジュール実体のファイル名にダイジェストが付加されていることから分かるように、後述するPropshaftの処理結果となっています。このインポート定義は、アプリケーションの共通テンプレートであるapp/views/layouts/application.html.erbにてjavascript_importmap_tagsヘルパーメソッドによって展開されます。
<!DOCTYPE html> <html> <head> <title>ReactApp</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag "application" %> (1) <%= javascript_importmap_tags %> (2) </head> <body> <%= yield %> </body> </html>
…略… <link rel="stylesheet" href="/assets/application-83523….css" /> (3) <script type="importmap" data-turbo-track="reload">{ (4) "imports": { …上記と同じなので省略… } }</script> <link rel="modulepreload" href="/assets/application-03d94….js"> (5) <script src="/assets/es-module-shims.min-5ae73….js" async="async" data-turbo-track="reload"></script> <script type="module">import "application"</script> …略…
ビュー中の(1)(2)は、それぞれスタイルシートの読み込みタグを展開するstylesheet_link_tagヘルパーメソッド、Import Mapsの読み込みタグを展開するjavascript_importmap_tagヘルパーメソッドです。(3)は、(1)に対応するスタイルシートの読み込みです。(4)以降は(2)に対応するImport Mapsのインポート定義と実際にファイルを読み込むscript要素です。(2)(4)にて、あらゆるマッピングがimportmap-railsでカプセル化されていることがお分かりいただけると思います。
Propshaftの役割
PropshaftもSprocketsと同様にアセットをpublic/assets以下にビルドしますが、以下の点が異なります。
- マニフェストファイルを使用しない
- ファイルを結合、圧縮しない
Sprocketsでは、マニフェストファイルであるapp/assets/config/manifest.jsから、バンドルするファイルを指定していました。Propshaftではマニフェストファイルを使用せず、config.assets.pathsパラメータで指定されるロード対象のパス以下にある全てのアセットが対象になります。また、ファイルは結合、圧縮しません。すでに紹介したように、ダイジェストを付加したファイルがそのままpublic/assets以下にコピーされます。そのため、PropshaftはSprocketsに比べて軽量です。
CSSファイル、JavaScriptモジュールがダイジェスト付きのファイル名で参照されていることは前項で紹介しましたが、配置した画像ファイルを含めて、これらがどのようになっているか見てみましょう。RAILS_ENV=production rails assets:precompileコマンドを実行すると、production環境のためにアセットがプリコンパイルされますので、public/assets以下を確認します。
- app/javascript/application.js ⇒ application-03d94….js
- app/javascript/components/react_hello.js ⇒ components/react_hello-902bd….js
- app/assets/stylesheets/application.css ⇒ application-83523….css
- app/assets/images/pig1.png ⇒ pig1-5541c….png
トランスパイラとしては、CSSファイル中のurl()関数を、ダイジェストを付加したファイル名に解決します。application-83523….cssに記述したurl()関数の引数も、以下のようにパスとダイジェスト付きのものに変換されます。
background-image: url("/assets/pig1-5541c….png");
アセットの参照は、path_to_asset、url_to_asset、stylesheet_link_tagなどのヘルパーメソッドで、論理パスで指定できるのはSprocketsと変わりません。
まとめ
今回は、importmap-railsとPropshaftを使ってReactアプリを開発する過程を通じて、これらのライブラリの目的と機能について紹介しました。次回は、同様のReactアプリをjsbundling-railsを使ってアプリケーションにバンドルするサンプルを紹介します。