ネットワーク タイムアウトの強制

ネットワーク接続が遅くても、接続が遅すぎるか、オンラインであるという嘘をついていることがあります。Service Worker が混在する状況では、ネットワーク ファーストのキャッシュ戦略では、ネットワークからレスポンスを取得するのに時間がかかりすぎたり、リクエストがハングし、エラーページが表示されるまでスピナーが無限に回転したりすることがあります。

いずれにせよ、一定期間の経過後にアセットまたはページの最後にキャッシュされたレスポンスにフォールバックする方がよい場合もありますが、Workbox が役立つ別の問題もあります。

networkTimeoutSeconds の使用

NetworkFirst 戦略または NetworkOnly 戦略を使用している場合、ネットワーク リクエストを強制的にタイムアウトさせることができます。これらの戦略には networkTimeoutSeconds オプションがあります。このオプションは、Service Worker がネットワーク レスポンスの到着を待つ秒数を指定するもので、その後、そのレスポンスの最後にキャッシュされたバージョンを返します。

// sw.js
import { NetworkFirst } from 'workbox-strategies';
import { registerRoute, NavigationRoute } from 'workbox-routing';

// Only wait for three seconds before returning the last
// cached version of the requested page.
const navigationRoute = new NavigationRoute(new NetworkFirst({
  networkTimeoutSeconds: 3,
  cacheName: 'navigations'
}));

registerRoute(navigationRoute);

上記のコードは、ネットワーク ファーストのナビゲーション リクエストを中止し、3 秒後に最後にキャッシュされたバージョンを使用するように Service Worker に指示します。これをナビゲーション リクエストで使用すると、以前にアクセスしたページの最後のキャッシュ レスポンスへのアクセスが保証されます。

しかし、アクセスしているページのキャッシュに古いレスポンスがない場合はどうすればよいでしょうか。そのような場合は、一般的なオフライン HTML ページへの代替応答を設定できます。

import {registerRoute, NavigationRoute} from 'workbox-routing';
import {NetworkOnly} from 'workbox-strategies';

// Hardcode the fallback cache name and the offline
// HTML fallback's URL for failed responses
const FALLBACK_CACHE_NAME = 'offline-fallback';
const FALLBACK_HTML = '/offline.html';

// Cache the fallback HTML during installation.
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(FALLBACK_CACHE_NAME).then((cache) => cache.add(FALLBACK_HTML)),
  );
});

// Apply a network-only strategy to navigation requests.
// If offline, or if more than five seconds pass before there's a
// network response, fall back to the cached offline HTML.
const networkWithFallbackStrategy = new NetworkOnly({
  networkTimeoutSeconds: 5,
  plugins: [
    {
      handlerDidError: async () => {
        return await caches.match(FALLBACK_HTML, {
          cacheName: FALLBACK_CACHE_NAME,
        });
      },
    },
  ],
});

// Register the route to handle all navigations.
registerRoute(new NavigationRoute(networkWithFallbackStrategy));

これがうまくいくのは、NetworkFirst 戦略で networkTimeoutSeconds を使用すると、タイムアウトが発生して URL のキャッシュ一致がない場合にハンドラがエラー レスポンスを返すためです。その場合、handlerDidError Workbox プラグインは代替として汎用のレスポンスを提供できます。

待ち時間が長すぎる場合

リクエスト(特にナビゲーション リクエスト)に対してタイムアウトを強制的に設定する場合は、ユーザーを長時間待たせないことと、タイムアウトが早すぎないようにバランスを取る必要があります。時間がかかりすぎると、低速の接続を使用しているユーザーがタイムアウトになる前にバウンスされるおそれがあります。タイムアウトが早すぎると、キャッシュから古いコンテンツが不必要に提供される可能性があります。

正解は「場合による」です。ブログなどのサイトを運営していて、コンテンツをあまり頻繁に更新していない場合、正しい答えはおそらく、キャッシュにあるものは何でも「新鮮」である可能性が高いためです。ただし、よりインタラクティブなウェブサイトやウェブアプリの場合は、もう少し長く待ち、Service Worker のキャッシュから古いデータを過度に頻繁に提供しないようにすることをおすすめします。

フィールドで指標を記録している場合は、Time to First Byte(TTFB)First Contentful Paint(FCP)のスコアの 75 パーセンタイルを調べて、ナビゲーション リクエストの待ち時間がどの程度長いかを把握します。そうすることで、線を引くべき場所を判断できます。