従来のプリフェッチ手法を Service Worker で補完。
サイトでタスクを実行するには、一般的に複数のステップが必要です。たとえば、e コマース ウェブサイトで商品を購入するには、商品の検索、結果のリストからのアイテムの選択、カートへのアイテムの追加、精算による操作の完了が含まれます。
技術的には、ページ間を移動するということは、ナビゲーション リクエストを行うことを意味します。原則として、有効期間の長い Cache-Control
ヘッダーを使用してナビゲーション リクエストの HTML レスポンスをキャッシュに保存することはおすすめしません。通常、これらの条件はネットワークを介して Cache-Control: no-cache
で満たされ、HTML と後続のネットワーク リクエストのチェーンが(妥当な)最新であることを確認する必要があります。
ユーザーが新しいページに移動するたびにネットワークにアクセスしなければならないということは、残念なことにそれぞれのナビゲーションが遅くなる可能性があることを意味します。少なくとも、確実に高速にはなりません。
ユーザーの操作を予想できる場合は、こうしたリクエストを高速化するため、事前にページやアセットをリクエストし、ユーザーがこれらのリンクをクリックするまで少しの間キャッシュに残しておくことができます。この手法はプリフェッチと呼ばれ、通常、プリフェッチするリソースを示す <link rel="prefetch">
タグをページに追加することで実装されます。
このガイドでは、従来のプリフェッチ手法を補完するために Service Worker を使用するさまざまな方法について説明します。
本番環境のケース
MercadoLibre は、ラテンアメリカ最大の e コマースサイトです。ナビゲーションを高速化するために、フローの一部に <link rel="prefetch">
タグを動的に挿入します。たとえば、リスティング ページでは、ユーザーがリスティングの一番下までスクロールしたらすぐに次の結果ページを取得します。
プリフェッチされたファイルは優先度「最も低い」でリクエストされ、リソースがキャッシュ可能かどうかに応じて HTTP キャッシュまたはメモリ キャッシュにブラウザによって異なる期間保存されます。たとえば Chrome 85 では、この値は 5 分です。リソースは 5 分間保持されます。その後は、リソースの通常の Cache-Control
ルールが適用されます。
Service Worker のキャッシュ保存を使用すると、プリフェッチ リソースの有効期間を 5 分以上延長できます。
たとえば、イタリアのスポーツ ポータルである Virgilio Sport は、Service Worker を使用して、ホームページで最も人気のある投稿をプリフェッチしています。また、2G 接続を使用するユーザーのプリフェッチを回避するため、Network Information API を使用します。
その結果、3 週間以上にわたって記事へのナビゲーションの読み込み時間が 78% 改善し、記事のインプレッション数が 45% 増加しました。
Workbox で事前キャッシュを実装する
次のセクションでは、Workbox を使用して Service Worker でさまざまなキャッシュ手法を実装する方法を示します。Service Worker はこのタスクを完全に Service Worker に委任することで、<link rel="prefetch">
の補完、またはその代わりとして使用できます。
1. 静的ページとページのサブリソースを事前キャッシュする
プレキャッシュとは、インストール中にファイルをキャッシュに保存する Service Worker の機能です。
次のケースでは、ナビゲーションを高速化するプリフェッチと同様の目的を達成するために、プレキャッシュが使用されます。
静的ページの事前キャッシュ
ビルド時に生成されたページ(例: about.html
、contact.html
)や、完全に静的なサイトの場合、サイトのドキュメントをプリキャッシュ リストに追加するだけで、ユーザーがアクセスするたびにキャッシュで利用できるようになります。
workbox.precaching.precacheAndRoute([
{url: '/about.html', revision: 'abcd1234'},
// ... other entries ...
]);
ページのサブリソースを事前キャッシュする
サイトのさまざまなセクションで使用される可能性のある静的アセット(JavaScript、CSS など)を事前キャッシュすることは、一般的なベスト プラクティスであり、プリフェッチのシナリオで効果を発揮します。
e コマースサイトでナビゲーションを高速化するには、リスティング ページで <link rel="prefetch">
タグを使用して、リスティング ページの最初の数件の商品の詳細ページをプリフェッチできます。商品ページのサブリソースを事前にキャッシュに保存している場合は、ナビゲーションをさらに高速化できます。
これを実装するには:
<link rel="prefetch">
タグをページに追加します。
<link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
- ページのサブリソースを Service Worker の事前キャッシュ リストに追加します。
workbox.precaching.precacheAndRoute([
'/styles/product-page.ac29.css',
// ... other entries ...
]);
2. プリフェッチ リソースの有効期間を延長する
前述のように、<link rel="prefetch">
はリソースを HTTP キャッシュにフェッチして一定期間保持します。その後、リソースの Cache-Control
ルールが適用されます。Chrome 85 では、この値は 5 分です。
Service Worker を使用すると、プリフェッチ ページの有効期間を延長できると同時に、それらのリソースをオフラインで使用できるようになります。
上記の例では、ワークボックスのランタイム キャッシュ戦略を使用して、商品ページのプリフェッチに使用される <link rel="prefetch">
を補完できます。
これを実装するには:
<link rel="prefetch">
タグをページに追加します。
<link rel="prefetch" href="/phones/smartphone-5x.html" as="document">
- 次のタイプのリクエストに対して、Service Worker にランタイム キャッシュ戦略を実装します。
new workbox.strategies.StaleWhileRevalidate({
cacheName: 'document-cache',
plugins: [
new workbox.expiration.Plugin({
maxAgeSeconds: 30 * 24 * 60 * 60, // 30 Days
}),
],
});
このケースでは、stale-while-revalidate 戦略を選択しています。この方式では、キャッシュとネットワークの両方から同時にページをリクエストできます。レスポンスは、キャッシュがある場合はキャッシュから取得され、それ以外の場合はネットワークから返されます。キャッシュは、リクエストが成功するたびにネットワーク レスポンスによって常に最新の状態に保たれます。
3. プリフェッチを Service Worker に委任する
ほとんどの場合、<link rel="prefetch">
を使用することをおすすめします。このタグは、プリフェッチをできるだけ効率的にするために設計されたリソースヒントです。
ただし、このタスクを完全に Service Worker に委任したほうがよい場合もあります。たとえば、クライアントサイドに表示される商品リスティング ページで最初の数件の商品をプリフェッチするには、API レスポンスに基づいて、複数の <link rel="prefetch">
タグをページに動的に挿入する必要があります。これにより、ページのメインスレッドが一時的に消費され、実装が難しくなる可能性があります。
このような場合は、「ページから Service Worker への通信方法」を使用して、プリフェッチを行うタスクを完全に Service Worker に委任します。このタイプの通信は、worker.postMessage() を使用して実現できます。
Workbox Window パッケージを使用すると、行われる基盤となる呼び出しの詳細を抽象化して、このタイプの通信を簡略化できます。
ワークボックス ウィンドウでのプリフェッチは、次の方法で実装できます。
- ページ内で、Service Worker を呼び出して、メッセージの種類とプリフェッチする URL のリストを渡します。
const wb = new Workbox('/sw.js');
wb.register();
const prefetchResponse = await wb.messageSW({type: 'PREFETCH_URLS', urls: […]});
- Service Worker で、プリフェッチする URL ごとに
fetch()
リクエストを発行するメッセージ ハンドラを実装します。
addEventListener('message', (event) => {
if (event.data.type === 'PREFETCH_URLS') {
// Fetch URLs and store them in the cache
}
});