なぜ今、`rel=”modulepreload”` なのか? ESモジュールの「読み込みの壁」を突破する技術
フロントエンドの現場でモダンなスタックを触っていると、一度は直面する「ウォーターフォール問題」。特にESモジュール(ESM)を多用するアプリケーションでは、メインのJSファイルを読み込んだ後に、その内部でインポートされている依存関係が順次読み込まれるため、ネットワークリクエストが階段状に発生し、LCP(Largest Contentful Paint)やTBT(Total Blocking Time)を悪化させる原因になります。
これまで私たちは `rel=”preload”` を使ってリソースを先取りしてきましたが、ESMに関しては少し事情が異なります。そこで登場するのが `rel=”modulepreload”` です。
今日は、この「一歩先を行く」リソースヒントについて、ブラウザの内部挙動を紐解きながら、明日から使える実践的な知見を共有しましょう。
—
1. なぜ単なる `preload` では不十分なのか
まず、前提を整理しましょう。「ただの `preload` でいいのでは?」と思うかもしれません。しかし、ESMには特有の難しさがあります。
一般的な `preload` は、ブラウザに対して「このファイルをダウンロードしておけ」という指示を出すだけです。しかし、ESMにはパース(解析)とコンパイルのフェーズが存在します。特に重要なのが「依存関係の解決」です。
通常の `preload` でJSを先読みしても、ブラウザは「それがモジュールなのか、単なるスクリプトなのか」を厳密に区別せず、単にバイナリとしてキャッシュするだけになりがちです。一方、`modulepreload` を使うと、ブラウザは以下の処理をバックグラウンドで先行して行います。
1. フェッチ: ファイルのダウンロード。
2. パース: JavaScriptとしての解析。
3. 依存関係の解決: モジュールが依存している他のモジュールの特定と取得。
つまり、`modulepreload` は単なるダウンロード以上の、「実行直前までの準備」を済ませてくれる頼もしい存在なのです。
—
2. ブラウザの裏側で何が起きているのか
`modulepreload` が指定されると、ブラウザのプリロードスキャナは即座にそのファイルを優先度の高いタスクとしてキューに入れます。
特筆すべきは、モジュールグラフの構築です。モジュールはツリー構造で依存関係を持っていますが、`modulepreload` を指定することで、ブラウザはメインのJSが実行される前に、その依存先ツリーの解析を一部開始できます。これにより、メインスレッドが空いた瞬間に実行可能な状態(Ready)で待機させることができるのです。
これは、大規模なアプリケーションで「画面が表示されたのに、機能が動くまで数秒かかる」という、あの忌々しいラグを解消するための強力な一手となります。
—
3. 実践:現場で即戦力となる実装パターン
では、具体的にどう書くべきか。基本的にはHTMLの `
` 内に記述します。ここで重要なのは、`crossorigin` 属性です。ESMはCORSの要件が厳しいため、たとえ同ドメインであっても `crossorigin` を明示的に指定しないと、正しくキャッシュが共有されず、二重読み込みが発生する可能性があるからです。
Tips: Viteなどのビルドツールとの付き合い方
最近のモダンなビルドツール(Viteなど)は、デフォルトでこの `modulepreload` を自動挿入してくれる設定になっています。手動で書く必要がないケースも多いですが、「動的インポート(Dynamic Import)」を使用している場合、ツールが依存関係を完璧に予測できないことがあります。
そんな時は、以下のようにスクリプトから動的にヒントを追加するのもテクニックの一つです。
/
- 特定のユーザーアクションの直前に、
- 必要なモジュールをプログラムからプリロードする
/
const preloadModule = (url) => {
const link = document.createElement(‘link’);
link.rel = ‘modulepreload’;
link.href = url;
link.crossOrigin = ‘anonymous’;
document.head.appendChild(link);
};
// 例えば、ユーザーが「編集ボタン」にホバーした瞬間に重いエディタ用モジュールを先行読み込み
document.querySelector(‘#edit-btn’).addEventListener(‘mouseenter’, () => {
preloadModule(‘/assets/js/editor-core.js’);
}, { once: true });
—
結びに:やりすぎには注意を
ここまで `modulepreload` の有用性を説いてきましたが、最後のアドバイスとして「何でもかんでも preload すれば速くなるわけではない」ということを忘れないでください。
リソースヒントはブラウザの帯域を専有します。重要度の低いファイルをプリロードしすぎると、本当に今すぐ必要なクリティカルパスのレンダリングを阻害してしまいます。
- LCPに関わるJSや、初期表示で必ず使うモジュールに絞る
- 動的インポートで読み込む「次の画面」に必要なモジュールを、ユーザーの遷移予測に基づいて先読みする
この2点を意識するだけで、あなたのプロダクトのパフォーマンスは間違いなく一段上の領域へ到達します。現場の泥臭い最適化こそ、フロントエンドエンジニアの腕の見せ所です。ぜひ、今日から試してみてください。

コメント