従来のプリフェッチ手法とサービス ワーカーを補完する。
サイトでタスクを実行するには、通常、いくつかのステップが必要です。たとえば、e コマース ウェブサイトで商品を購入するには、商品の検索、結果のリストからの商品の選択、カートへの商品の追加、決済によるオペレーションの完了などがあります。
技術的な用語で言えば、ページ間を移動することは、ナビゲーション リクエストを行うことを意味します。原則として、ナビゲーション リクエストの HTML レスポンスをキャッシュに保存するために、有効期間の長い Cache-Control
ヘッダーは使用しません。通常は、HTML と後続のネットワーク リクエストのチェーンが(合理的に)最新であることを確認するために、Cache-Control: no-cache
を使用してネットワーク経由で処理する必要があります。ユーザーが新しいページに移動するたびにネットワークにアクセスする必要があるため、各ページへの移動が遅くなる可能性があります。少なくとも、信頼性の高い高速なページ遷移は実現できません。
これらのリクエストを高速化するには、ユーザーの操作を予測できる場合は、これらのページとアセットを事前にリクエストし、ユーザーがリンクをクリックするまで短時間キャッシュに保持します。この手法はプリフェッチと呼ばれ、通常はページに <link rel="prefetch">
タグを追加して、プリフェッチするリソースを指定することで実装されます。
このガイドでは、従来のプリフェッチ手法の補完として サービス ワーカーを使用するさまざまな方法について説明します。
本番環境のケース
MercadoLibre は、ラテンアメリカ最大の e コマース サイトです。ナビゲーションを高速化するために、フロー内の一部に <link rel="prefetch">
タグを動的に挿入します。たとえば、リスティング ページでは、ユーザーがリスティングの一番下までスクロールするとすぐに、次の検索結果ページが取得されます。
プリフェッチされたファイルは「低」の優先度でリクエストされ、(リソースがキャッシュに保存可能かどうかに応じて)HTTP キャッシュまたはメモリキャッシュにブラウザによって異なる時間だけ保存されます。たとえば、Chrome 85 ではこの値は 5 分です。リソースは 5 分間保持され、その後はリソースに対する通常の Cache-Control
ルールが適用されます。
Service Worker のキャッシュを使用すると、プリフェッチ リソースの有効期間を 5 分よりも長くすることができます。
たとえば、イタリアのスポーツ ポータル Virgilio Sport は、サービス ワーカーを使用して、ホームページで最も人気のある投稿をプリフェッチしています。また、2G 接続を使用しているユーザーのプリフェッチを回避するため、Network Information API が使用されます。
その結果、Virgilio Sport では 3 週間にわたる観察で、記事へのナビゲーションの読み込み時間が78% 改善され、記事のインプレッション数が45% 増加しました。
Workbox でプリキャッシュを実装する
次のセクションでは、Workbox を使用して、<link rel="prefetch">
の補完として使用できる、またはこのタスクをサービス ワーカーに完全に委任することで、<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">
- ページのサブリソースをサービス ワーカーのプリキャッシュ リストに追加します。
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">
を、Workbox ランタイム キャッシュ戦略で補完できます。
実装方法は次のとおりです。
- ページに
<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
}),
],
});
この場合、再検証中の古いデータを使用する戦略を選択しました。この方法では、キャッシュとネットワークの両方から並行してページをリクエストできます。レスポンスは、キャッシュから取得されます(キャッシュが利用可能な場合)。キャッシュが利用できない場合は、ネットワークから取得されます。キャッシュは、リクエストが成功するたびにネットワーク レスポンスに合わせて常に最新の状態に保たれます。
3. プリフェッチを Service Worker に委任する
ほとんどの場合、<link rel="prefetch">
を使用することをおすすめします。このタグは、プリフェッチをできるだけ効率的にするためのリソースヒントです。
ただし、このタスクを完全に Service Worker に委任した方がよい場合もあります。たとえば、クライアントサイドでレンダリングされた商品リスティング ページで最初の数件の商品をプリフェッチするには、API レスポンスに基づいて、ページに複数の <link rel="prefetch">
タグを動的に挿入する必要があります。これにより、ページのメインスレッドが一時的に消費され、実装が困難になります。
このような場合は、「ページから Service Worker への通信戦略」を使用して、プリフェッチのタスクを Service Worker に完全に委任します。このタイプの通信は、worker.postMessage() を使用して実現できます。
Workbox Window パッケージは、このタイプの通信を簡素化し、実行される基盤となる呼び出しの多くの詳細を抽象化します。
ワークボックス ウィンドウを使用したプリフェッチは、次の方法で実装できます。
- ページで: メッセージのタイプとプリフェッチする URL のリストを渡して Service Worker を呼び出します。
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
}
});