フォールバック レスポンスの管理

状況によっては、ユーザーがオフラインの場合に備えて、フォールバック レスポンスをキャッシュに保存することをおすすめします。ネットワーク ファーストやステイル更新中再検証などの戦略で提供されるキャッシュ動作の代わりに、フォールバックを実装できます。

フォールバックとは、リクエストに失敗したときにブラウザがデフォルトで提供するものよりも優れたプレースホルダである、画一的な汎用レスポンスです。たとえば次のようなものがあります。

  • 「画像が表示されない」プレースホルダの代わりに表示されます。
  • 標準の「ネットワークに接続できません」というページの代わりとなる HTML リンクです。

オフライン ページのみ

カスタムのオフライン HTML ページを用意し、それ以外何もしない場合は、以下の基本的な方法を使用できます。

import {offlineFallback} from 'workbox-recipes';
import {setDefaultHandler} from 'workbox-routing';
import {NetworkOnly} from 'workbox-strategies';

setDefaultHandler(new NetworkOnly());

offlineFallback();

上記のコードは、setDefaultHandler を使用して、すべてのルートのデフォルトとしてネットワークのみの戦略を使用します。次に、offlineFallback レシピを実行して、エラーが発生した場合にオフライン フォールバックを提供します。レシピでは、オフラインの代替 HTML ファイルの名前が offline.html で、ウェブサーバーのルートから提供されることを前提としています。

包括的なフォールバック

ネットワーク障害やキャッシュミスが発生すると、workbox-strategies が提供するキャッシュ戦略によって一貫して拒否されます。これにより、1 つのハンドラ関数で発生したすべての障害に対処するようにグローバルな「キャッチ」ハンドラを設定するパターンが促進され、request.destination 値ごとに異なるフォールバックを提供できるようになります。

次の例では、workbox-recipeswarmStrategyCache レシピを使用し、ランタイム キャッシュに事前にキャッシュされたアイテムを提供するキャッチ ハンドラを設定しています。ただし、アプリケーションによっては、フォールバックをプレキャッシュしたほうがよい場合があります。

import {warmStrategyCache} from 'workbox-recipes';
import {setDefaultHandler, setCatchHandler} from 'workbox-routing';
import {CacheFirst, StaleWhileRevalidate} from 'workbox-strategies';

// Fallback assets to cache
const FALLBACK_HTML_URL = '/offline.html';
const FALLBACK_IMAGE_URL = '/images/image-not-found.jpg';
const FALLBACK_STRATEGY = new CacheFirst();

// Warm the runtime cache with a list of asset URLs
warmStrategyCache({
  urls: [FALLBACK_HTML_URL, FALLBACK_IMAGE_URL],
  strategy: FALLBACK_STRATEGY,
});

// Use a stale-while-revalidate strategy to handle requests by default.
setDefaultHandler(new StaleWhileRevalidate());

// This "catch" handler is triggered when any of the other routes fail to
// generate a response.
setCatchHandler(async ({request}) => {
  // The warmStrategyCache recipe is used to add the fallback assets ahead of
  // time to the runtime cache, and are served in the event of an error below.
  // Use `event`, `request`, and `url` to figure out how to respond, or
  // use request.destination to match requests for specific resource types.
  switch (request.destination) {
    case 'document':
      return FALLBACK_STRATEGY.handle({event, request: FALLBACK_HTML_URL});

    case 'image':
      return FALLBACK_STRATEGY.handle({event, request: FALLBACK_IMAGE_URL});

    default:
      // If we don't have a fallback, return an error response.
      return Response.error();
  }
});

次に、Workbox のビルドツールで injectManifest を使用してフォールバック レスポンスが事前キャッシュに保存され、matchPrecache メソッドでエラーが発生した場合のフォールバックとして機能します。

import {matchPrecache, precacheAndRoute} from 'workbox-precaching';
import {setDefaultHandler, setCatchHandler} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';

// Optional: use the injectManifest mode of one of the Workbox
// build tools to precache a list of URLs, including fallbacks.
precacheAndRoute(self.__WB_MANIFEST);

// Use a stale-while-revalidate strategy to handle requests by default.
setDefaultHandler(new StaleWhileRevalidate());

// This "catch" handler is triggered when any of the other routes fail to
// generate a response.
setCatchHandler(async ({request}) => {
  // Fallback assets are precached when the service worker is installed, and are
  // served in the event of an error below. Use `event`, `request`, and `url` to
  // figure out how to respond, or use request.destination to match requests for
  // specific resource types.
  switch (request.destination) {
    case 'document':
      // FALLBACK_HTML_URL must be defined as a precached URL for this to work:
      return matchPrecache(FALLBACK_HTML_URL);

    case 'image':
      // FALLBACK_IMAGE_URL must be defined as a precached URL for this to work:
      return matchPrecache(FALLBACK_IMAGE_URL);

    default:
      // If we don't have a fallback, return an error response.
      return Response.error();
  }
});

2 つ目の代替設定の使用例として、ページが事前にキャッシュされているものの、ページからリクエストされた画像(または他のアセット)はキャッシュされていない場合です。ユーザーがオフラインの場合でも、キャッシュからページを読み取ることはできますが、ネットワーク エラーが発生した場合は、フォールバック プレースホルダまたは代替機能を提供できます。

ランタイム キャッシュのウォーム

Workbox は、プレキャッシュ用とランタイム キャッシュ用に別々のキャッシュを保持します。プリキャッシュに依存せずに事前にキャッシュしたい状況があります。これは、プレキャッシュ マニフェストを更新すると、更新された Service Worker をデプロイする必要があるためです。

ランタイム キャッシュにアセットを事前に準備するには、workbox-recipeswarmStrategyCache レシピを使用します。内部的には、この戦略は Service Worker の install イベントで Cache.addAll を呼び出します。

import {warmStrategyCache} from 'workbox-recipes';
import {CacheFirst} from 'workbox-strategies';

// This can be any strategy, CacheFirst used as an example.
const strategy = new CacheFirst();
const urls = [
  '/offline.html',
];

warmStrategyCache({urls, strategy});

まとめ

リクエストの失敗に対するフォールバック レスポンスの管理には多少の手間がかかりますが、あらかじめ計画を立てておくと、ユーザーがオフラインのときにもある程度のコンテンツや機能を提供するようにウェブアプリを設定できます。