処理中

プログレッシブ ウェブアプリの重要な側面は、信頼性にあります。アセットをすばやく読み込んでユーザーのエンゲージメントを維持し、ネットワーク状況が悪くてもすぐにフィードバックを提供できます。これは以下のようにして可能になります。Service Worker の fetch イベントのおかげです。

取得イベント

対応ブラウザ

  • 40
  • 17
  • 44
  • 11.1

ソース

fetch イベントを使用すると、同一オリジン リクエストとクロスオリジン リクエストの両方について、Service Worker のスコープで PWA から送信されたすべてのネットワーク リクエストをインターセプトできます。ナビゲーションやアセットのリクエストに加えて、インストール済みの Service Worker から取得することで、サイトの最初の読み込み後のページ訪問をネットワーク呼び出しなしでレンダリングできます。

fetch ハンドラは、URL や HTTP ヘッダーなど、アプリからのすべてのリクエストを受け取り、その処理方法をアプリ デベロッパーが決定できるようにします。

Service Worker は、クライアントとネットワークの間に配置されます。

Service Worker は、リクエストをネットワークに転送したり、以前にキャッシュに保存されたレスポンスで応答したり、新しいレスポンスを作成したりできます。選択は任意です。たとえば、次のような例が考えられます。

self.addEventListener("fetch", event => {
    console.log(`URL requested: ${event.request.url}`);
});

リクエストへの対応

Service Worker に届いたリクエストには、2 つの対処方法があります。リクエストを無視してネットワークに送信できるようにするか、リクエストに応答するかです。Service Worker 内からリクエストに応答することで、ユーザーがオフラインの場合でも、何をどのように PWA に返すかを選択できます。

着信リクエストに応答するには、次のように fetch イベント ハンドラ内から event.respondWith() を呼び出します。

// fetch event handler in your service worker file
self.addEventListener("fetch", event => {
    const response = .... // a response or a Promise of response
    event.respondWith(response);
});

respondWith() は同期的に呼び出し、Response オブジェクトを返す必要があります。ただし、非同期呼び出し内など、フェッチ イベント ハンドラの完了後に respondWith() を呼び出すことはできません。完全なレスポンスを待機する必要がある場合は、Response で解決される Promise を respondWith() に渡すことができます。

レスポンスの作成

Fetch API を使用すると、JavaScript コードで HTTP レスポンスを作成できます。作成したレスポンスは、Cache Storage API を使用してキャッシュに保存でき、ウェブサーバーから送信されたかのように返すことができます。

レスポンスを作成するには、新しい Response オブジェクトを作成し、その本文と、ステータスやヘッダーなどのオプションを設定します。

const simpleResponse = new Response("Body of the HTTP response");

const options = {
   status: 200,
   headers: {
    'Content-type': 'text/html'
   }
};
const htmlResponse = new Response("<b>HTML</b> content", options)

キャッシュからの応答

Service Worker から HTTP レスポンスを処理する方法がわかったので、次に Caching Storage インターフェースを使用してデバイスにアセットを保存します。

Cache Storage API を使用すると、PWA から受信したリクエストがキャッシュで利用可能かどうかを確認し、利用可能であれば respondWith() に応答できます。それには、まずキャッシュ内を検索する必要があります。最上位の caches インターフェースで利用可能な match() 関数は、オリジン内のすべてのストア、または開いている単一のキャッシュ オブジェクトを検索します。

match() 関数は、HTTP リクエストまたは URL を引数として受け取り、対応するキーに関連付けられた Response で解決される Promise を返します。

// Global search on all caches in the current origin
caches.match(urlOrRequest).then(response => {
   console.log(response ? response : "It's not in the cache");
});

// Cache-specific search
caches.open("pwa-assets").then(cache => {
  cache.match(urlOrRequest).then(response => {
    console.log(response ? response : "It's not in the cache");
  });
});

キャッシュ戦略

ブラウザのキャッシュからのみファイルを提供するのが、すべてのユースケースに適しているわけではありません。たとえば、ユーザーやブラウザがキャッシュを削除することがあります。そのため、PWA 用のアセットを配信するための独自の戦略を定義する必要があります。1 つのキャッシュ戦略に縛られるわけではありません。URL パターンごとに異なるパターンを定義できます。たとえば、最小の UI アセット、API 呼び出し、画像およびデータ URL の戦略を用意できます。これを行うには、ServiceWorkerGlobalScope.onfetchevent.request.url を読み取り、正規表現または URL パターンを使用して解析します。(現時点では、URL パターンはすべてのプラットフォームでサポートされているわけではありません)。

最も一般的な戦略は次のとおりです。

キャッシュ ファースト
まず、キャッシュに保存されたレスポンスを検索し、見つからない場合はネットワークにフォールバックします。
ネットワーク ファースト
まずネットワークからレスポンスをリクエストし、レスポンスが返されない場合はキャッシュ内のレスポンスを確認します。
再検証中に最新でない
キャッシュからレスポンスを返し、バックグラウンドで最新バージョンをリクエストして、次回アセットがリクエストされたときにキャッシュに保存します。
ネットワークのみ
常にネットワークからのレスポンスを返すか、エラーを返します。キャッシュが参照されることはありません。
キャッシュのみ
常にキャッシュからのレスポンスを返すか、エラーを返します。このネットワークがコンサルトされることはありません。この戦略で配信されるアセットは、リクエストする前にキャッシュに追加しておく必要があります。

最初にキャッシュ

この方法を使用すると、Service Worker はキャッシュ内で一致するリクエストを探し、キャッシュされていれば、対応するレスポンスを返します。それ以外の場合は、ネットワークからレスポンスを取得します(必要に応じて、今後の呼び出しのためにキャッシュを更新します)。キャッシュ レスポンスもネットワーク レスポンスもない場合、リクエストはエラーになります。ネットワークにアクセスせずにアセットを配信するほうが速い傾向があるため、この戦略では鮮度よりもパフォーマンスが優先されます。

キャッシュ ファースト戦略

self.addEventListener("fetch", event => {
   event.respondWith(
     caches.match(event.request)
     .then(cachedResponse => {
       // It can update the cache to serve updated content on the next request
         return cachedResponse || fetch(event.request);
     }
   )
  )
});

ネットワーク ファースト

この戦略はキャッシュ ファーストの戦略を反映したものです。ネットワークからリクエストを実行できるかどうかを確認し、実行できない場合はキャッシュから取得しようとします。キャッシュを優先します。ネットワーク レスポンスもキャッシュ レスポンスもない場合、リクエストはエラーになります。通常、ネットワークからのレスポンスの取得はキャッシュからのレスポンス取得よりも遅いため、この戦略ではパフォーマンスよりも更新されたコンテンツが優先されます。

ネットワーク ファーストの戦略

self.addEventListener("fetch", event => {
   event.respondWith(
     fetch(event.request)
     .catch(error => {
       return caches.match(event.request) ;
     })
   );
});

再検証中に最新でない

古い再検証戦略では、キャッシュに保存されたレスポンスがすぐに返され、ネットワークの更新がチェックされます。見つかった場合は、キャッシュに保存されたレスポンスが置き換えられます。この戦略では、常にネットワーク リクエストを実行します。これは、キャッシュに保存されたリソースが見つかった場合でも、キャッシュに保存されたリソースをネットワークから受信したリソースで更新し、次のリクエストで更新されたバージョンを使用するためです。したがって、この戦略では、キャッシュ ファースト戦略が迅速に提供され、バックグラウンドでキャッシュを更新できるというメリットがあります。

古くなった再検証戦略

self.addEventListener('fetch', event => {
  event.respondWith(
    caches.match(event.request).then(cachedResponse => {
        const networkFetch = fetch(event.request).then(response => {
          // update the cache with a clone of the network response
          const responseClone = response.clone()
          caches.open(url.searchParams.get('name')).then(cache => {
            cache.put(event.request, responseClone)
          })
          return response
        }).catch(function (reason) {
          console.error('ServiceWorker fetch failed: ', reason)
        })
        // prioritize cached response over network
        return cachedResponse || networkFetch
      }
    )
  )
})

ネットワークのみ

ネットワークのみの戦略は、Service Worker や Cache Storage API を使用しない場合のブラウザの動作に似ています。リクエストでは、ネットワークから取得できるリソースのみが返されます。これは多くの場合、オンラインのみの API リクエストなどのリソースに役立ちます。

ネットワークのみの戦略

キャッシュのみ

「キャッシュのみ」の戦略では、リクエストがネットワークに送信されることはありません。すべての受信リクエストに対して、事前に設定されたキャッシュ アイテムが返されます。次のコードでは、fetch イベント ハンドラとキャッシュ ストレージの match メソッドを使用して、キャッシュのみに応答します。

self.addEventListener("fetch", event => {
   event.respondWith(caches.match(event.request));
});

キャッシュのみの戦略。

カスタム戦略

上記は一般的なキャッシュ戦略ですが、Service Worker とリクエストの処理方法はお客様自身で管理する必要があります。ニーズに合わない場合は、独自に作成します。

たとえば、タイムアウト付きのネットワーク ファースト戦略を使用して、設定したしきい値内にレスポンスが表示される場合にのみ、更新されたコンテンツを優先させることができます。また、キャッシュに保存されたレスポンスをネットワーク レスポンスと統合し、Service Worker から複雑なレスポンスを作成することもできます。

アセットを更新しています

PWA のキャッシュ アセットを最新の状態に保つことは簡単ではありません。古くなった再検証戦略もその一つの方法ですが、それだけではありません。更新の章では、アプリのコンテンツとアセットを最新の状態に保つさまざまな方法について説明します。

リソース