はじめに
本記事は錆びかけたRailsの知識を頑張ってアップデートするアドベントカレンダー11日目です。
今日は以前の記事(Rails5、Rails6、Rails7の主な特徴まとめ)で念頭に入れたImport Maps
について調べました。
背景
筆者は現在、Rails7のHotWireを学んでいます。
HotWireを学ぶ教材として、猫Rails様の書かれた猫でもわかるHotwire入門 Turbo編を読み手を動かしています。
その中ではImport Mapsは使われていなかったのですが、Rails7の目玉機能としてこちらを取り扱うことが挙げられていたので、調査しました。
Import Mapsの解説
Import Mapsとは?
JavaScriptのモジュールを読み込む際、読み込み元のモジュールについて名前をつけて読み込むことができる仕組みです。これはブラウザがJavaScriptを読み込む際の比較的新しい構文で、全てのブラウザでサポートされているわけではありません。
Import Mapsの使い方
Import Mapsは<script>
タグ内にJSON形式で記述します。以下は、Import Mapsの例です。
例えば、以下のようなJavaScriptを読み込みたかったとします。
// /js/utils.js
export function greet(name) {
return `Hello, ${name}!`;
}
この時、読み込む側のファイルには以下のように書くことができます。
<!DOCTYPE html>
<html>
<head>
<title>Import Maps Example</title>
<script type="importmap">
{
"imports": {
"my-utils": "/js/utils.js",
"external-module": "https://cdn.example.com/external-module.js"
}
}
</script>
</head>
<body>
<script type="module" src="/js/main.js"></script>
</body>
</html>
こちらの例では、JavaScriptをどこから読み込むのかを指定し、またその読み込んだJavaScriptのモジュールをなんという名前で利用できるようにするかを決めています。
そして、読み込んだJavaScriptのモジュールは以下のように利用できます。
// /js/main.js
import { greet } from 'my-utils';
import { someFunction } from 'external-module';
console.log(greet('Alice')); // ローカルモジュールの関数を使用
someFunction(); // 外部モジュールの関数を使用
特定のパスにおいたJavaScriptファイル(内のモジュール)に名前をつけて、main.js
で呼び出している様子がわかります。
このように、「JavaScriptファイルをなんという名前で呼び出せるようにするか決めること」をマッピング
と呼びます。
Import Mapsを使うメリット
Import Mapsを使うことで、複数のファイルから読み込まれるJavaScriptのパスが変わった時に書き換える場所を一箇所だけにすることができます。
例を挙げて説明します。例えば、以下のように複数のファイルから読み込まれているモジュールがあったとします。
// /js/module1.js
import lodash from 'lodash';
// 何らかの処理...
// /js/module2.js
import lodash from 'lodash';
// 何らかの処理...
この時、Import Mapsには以下のように書かれています。
<script type="importmap">
{
"imports": {
"lodash": "/path/to/lodash.js"
}
}
</script>
これで、もしlodash.jsのパスが変わったとしてもImport Mapsのマッピング部分だけ書き換えれば修正できます。
直接各ファイルにlodash.jsのパスが書かれていた場合、読み込んでいるファイルの数だけパス部分を書き換える必要があり不便です。
Ruby on RailsにおけるImport Mapsの立ち位置
Rails7から、デフォルトでimportmap-rails
というgemが使われるようになりました。
Import Mapsを利用して、以下のことを実現します。
MutationObserverでDOM監視:
WebページのDOM(Document Object Model、つまりページの構造)の変更をMutationObserverを使って監視します。これにより、ページ上で新しい要素が追加されたり、既存の要素が変更されたりしたときに、それを検知できます。
Stimulusコントローラの適用:
検知されたDOMの変更に応じて、特定のStimulusコントローラが必要であると判断された場合、そのコントローラに関連するJavaScriptコードをその時点でページに読み込みます。
非同期読み込み(Dynamic Import):
この読み込みは非同期で行われます。つまり、ページの初期ロード時にはそのコントローラのコードは含まれておらず、必要になった時点で初めて読み込まれるということです。
この方法では、ページに初めから全てのJavaScriptを読み込まずに、実際に必要なコントローラのコードのみを読み込むため、ページのパフォーマンスが向上します。HTTP/2の恩恵により、これらのリクエストは非同期で効率的に処理されます。
Rails6までとの違い
Webサイトのパフォーマンスを上げるという観点で、読み込むJavaScriptファイルの大きさはなるべく小さくすべきです。
Rails6までは主にWebpack
というモジュールバンドラーを利用し、パッケージサイズを小さくしようとしていました。
これは、JavaScriptのファイルを解析し、挙動はそのままで不要な空白、改行を圧縮するというアプローチでした。
そのWebサイトで利用するJavaScriptを全て一度に読み込むような形です。
対してimportmap-rails
を利用した方法では、そもそもページに読み込むJavaScriptファイルの数を最適化するというアプローチを取っています。
果たしてどちらの方がWebサイトのパフォーマンスが出るのでしょうか?
終わりに
Import Mapsの仕様とともに、Rails7においてImport Mapsがどのように利用されているのかやその意図についてまとめました。