ブラウザという巨大なブラックボックスを操る我々にとって、リソースの読み込み戦略は「単なる最適化」ではない。それは、ユーザーの体験時間をいかに支配し、ネットワークという気まぐれな帯域をいかに効率的に食い尽くすかという、高度なリソース管理の戦いそのものだ。
今回は、現場でしばしば混同され、誤用によって逆にパフォーマンスを殺している`preload`と`prefetch`の深淵に触れていこう。
—
1. Preload: 「今すぐ必要なのに、なぜか後回しにされる」への強硬手段
`preload`(``)は、ブラウザの先読みスキャナ(Preload Scanner)が即座に見つけられないリソースを、強制的に優先順位のトップへ引き上げるための命令だ。
なぜこれが重要なのか
ブラウザのレンダリングパイプラインにおいて、CSSを解析している間はJSの実行が止まり、JSが解析されている間はDOM構築が止まる。特に、「CSSの中で定義されたWebフォント」や「JSから動的にインポートされるバックグラウンド画像」などは、ブラウザがその重要性に気づくのが遅れる。結果、FOUC(Flash of Unstyled Content)やレイアウトシフトが発生する。
`preload`はブラウザに対して、「これは最優先でダウンロードしろ。たとえ他の何が待たされようとも」と指示を出す。
実務上の落とし穴:メモリ効率と競合
ここで重要なのは、「何でもかんでもpreloadすればいい」という勘違いだ。
`preload`は、現在ページのクリティカルパスに存在するリソースを対象にする。もし、将来のページで使うリソースを`preload`してしまうと、現在のページの読み込みに必要な帯域を圧迫し、結果としてLCP(Largest Contentful Paint)を遅延させるという本末転倒な事態を招く。
—
2. Prefetch: 「未来への投資」という名の低優先度バックグラウンド処理
一方、`prefetch`は全く性質が異なる。これは「ユーザーが次に遷移する可能性が高いページのリソース」を、ネットワークがアイドル状態の時にこっそりダウンロードしておくための戦略だ。
アーキテクチャ的な差異
`prefetch`されたリソースは、ブラウザのキャッシュメモリに格納されるが、その優先度は極めて低い。もしユーザーが即座に別のアクションを起こせば、`prefetch`は中断されるか、後回しにされる。これは、「現在のページの快適さを一切犠牲にしない」というブラウザ側の良心的な設計に基づいている。
実践的な使い分け
- Preload: 現在のページで「今すぐ」必要。失敗するとレンダリングが止まる。
- Prefetch: 次のページで「たぶん」必要。失敗しても今のページには何の影響もない。
—
3. 実践:現場で戦うための戦略的コードパターン
モダンなSPAアーキテクチャでは、これを「魔法」ではなく「制御」として扱う必要がある。以下は、動的なリソースの先読みを適切に行うためのパターンだ。
/
- ユーザーがリンクにホバーした瞬間、または特定の条件でprefetchを実行するユーティリティ
/
function prefetchResource(url, asType = ‘document’) {
if (document.querySelector(`link[href=”${url}”]`)) return;
const link = document.createElement(‘link’);
link.rel = ‘prefetch’;
link.href = url;
link.as = asType;
document.head.appendChild(link);
}
// 例:サイドバーの「詳細ページ」へのリンクをホバーした時にprefetchを実行
const detailLink = document.querySelector(‘.detail-link’);
detailLink.addEventListener(‘mouseover’, () => {
prefetchResource(‘/api/data/detail-123.json’, ‘fetch’);
}, { once: true }); // 一度実行すれば十分
—
4. スペシャリストとしての戒め:重大なバグを避けるために
最後に、これだけは覚えておいてほしい。「リソースの二重ダウンロード」だ。
`preload`を適切に定義していても、JS側で同じリソースを読み込む際に、fetchのモード(CORS設定など)が一致していないと、ブラウザは「これは別物だ」と判断し、二重にネットワークリクエストを投げる。
- CORS: フォントなどのリソースを`preload`する際は、`crossorigin`属性を必ず含めること。これを忘れると、たとえ同じURLであってもブラウザは二度ダウンロードする。メモリも帯域もドブに捨てることになる。
- 動的インポートとの相性: Webpackなどのバンドラーが生成する`async chunk`と、手動で書いた`preload`が競合していないか、Chrome DevToolsの「Network」タブで「Initiator」を確認する癖をつけること。
結論
`preload`は「現在のページを完成させるための外科手術」であり、`prefetch`は「未来の体験を加速させるための投資」だ。
我々エンジニアがすべきことは、ブラウザのレンダリングパイプラインを止める「ボトルネック」を特定し、`preload`でその芽を摘み取り、ユーザーの予測行動を`prefetch`で先回りすることに他ならない。
技術に魔法はない。あるのは、ブラウザというエンジンの特性をどこまで深く理解し、泥臭くチューニングしたかという事実だけだ。さあ、次はどのリソースを最適化しようか?

コメント