ワークボックス ルーティング

Service Worker は、ページのネットワーク リクエストをインターセプトできます。キャッシュされたコンテンツ、ネットワークのコンテンツ、Service Worker で生成されたコンテンツなどを使用してブラウザに応答します。

workbox-routing は、レスポンスを提供するさまざまな関数にこれらのリクエストを簡単に「ルーティング」できるようにするモジュールです。

ルーティングの実行方法

ネットワーク リクエストによって Service Worker のフェッチ イベントが発生すると、workbox-routing は指定されたルートとハンドラを使用してリクエストに応答しようとします。

ワークボックスのルーティング図

上述の主な注意点は次のとおりです。

  • リクエストの方法は重要です。デフォルトでは、Route は GET リクエストに対して登録されます。他のタイプのリクエストをインターセプトする場合は、メソッドを指定する必要があります。

  • ルートの登録順序は重要です。リクエストを処理できる複数の Route が登録されている場合は、最初に登録された Route がリクエストの処理に使用されます。

ルートを登録する方法はいくつかあり、コールバック、正規表現、Route インスタンスを使用できます。

ルートでの照合と処理

ワークボックスの「route」は、ルートがリクエストに一致するかどうかを判断する「マッチング」関数と、リクエストを処理してレスポンスを返す「処理」関数の 2 つの機能にすぎません。

Workbox にはマッチングと処理を行うヘルパーが用意されていますが、別の動作が必要な場合は、カスタムの一致とハンドラ関数を作成するのが最適です。

一致コールバック関数には、ExtendableEventRequest、信頼できる値を返すことで照合できる URL オブジェクトが渡されます。簡単な例として、次のように特定の URL を照合できます。

const matchCb = ({url, request, event}) => {
  return url.pathname === '/special/url';
};

url または request を調査またはテストすることで、ほとんどのユースケースに対応できます。

ハンドラ コールバック関数には、同じ ExtendableEventRequestURL オブジェクトと、params 値(match 関数によって返される値)が渡されます。

const handlerCb = async ({url, request, event, params}) => {
  const response = await fetch(request);
  const responseBody = await response.text();
  return new Response(`${responseBody} <!-- Look Ma. Added Content. -->`, {
    headers: response.headers,
  });
};

ハンドラは、Response に解決される Promise を返す必要があります。この例では、asyncawait を使用します。内部で、返される Response 値は Promise にラップされます。

これらのコールバックは次のように登録できます。

import {registerRoute} from 'workbox-routing';

registerRoute(matchCb, handlerCb);

唯一の制限は、「match」コールバックは同期的に真の値を返す必要があり、非同期処理は実行できません。これは、Router がフェッチ イベントに同期的に応答するか、他のフェッチ イベントにフォールバックする必要があるためです。

通常、「ハンドラ」コールバックは、workbox-strategies によって提供される方法のいずれかを次のように使用します。

import {registerRoute} from 'workbox-routing';
import {StaleWhileRevalidate} from 'workbox-strategies';

registerRoute(matchCb, new StaleWhileRevalidate());

このページでは workbox-routing を中心に説明しますが、ワークボックス戦略に関するこれらの戦略の詳細もご覧ください。

正規表現のルートの登録方法

一般的には、「match」コールバックの代わりに正規表現を使用します。Workbox を使用すると、次のように簡単に実装できます。

import {registerRoute} from 'workbox-routing';

registerRoute(new RegExp('/styles/.*\\.css'), handlerCb);

同じ送信元からのリクエストの場合、リクエストの URL が正規表現と一致する限り、この正規表現は一致します。

  • https://example.com/styles/main.css
  • https://example.com/styles/nested/file.css
  • https://example.com/nested/styles/directory.css

ただし、クロスオリジン リクエストの場合、正規表現は URL の先頭と一致する必要があります。これは、正規表現 new RegExp('/styles/.*\\.css') を使用するとサードパーティの CSS ファイルを照合できる可能性が低いためです。

  • https://cdn.third-party-site.com/styles/main.css
  • https://cdn.third-party-site.com/styles/nested/file.css
  • https://cdn.third-party-site.com/nested/styles/directory.css

この動作が必要な場合は、正規表現が URL の先頭と一致するようにする必要があります。https://cdn.third-party-site.com のリクエストを照合する場合は、正規表現 new RegExp('https://cdn\\.third-party-site\\.com.*/styles/.*\\.css') を使用できます。

  • https://cdn.third-party-site.com/styles/main.css
  • https://cdn.third-party-site.com/styles/nested/file.css
  • https://cdn.third-party-site.com/nested/styles/directory.css

ローカルとサードパーティの両方に一致する場合は、正規表現の先頭にワイルドカードを使用できますが、ウェブアプリで予期しない動作が発生しないように注意する必要があります。

ナビゲーション ルートを登録する方法

シングルページ アプリの場合は、NavigationRoute を使用して、すべてのナビゲーション リクエストに対して特定のレスポンスを返すことができます。

import {createHandlerBoundToURL} from 'workbox-precaching';
import {NavigationRoute, registerRoute} from 'workbox-routing';

// This assumes /app-shell.html has been precached.
const handler = createHandlerBoundToURL('/app-shell.html');
const navigationRoute = new NavigationRoute(handler);
registerRoute(navigationRoute);

ユーザーがブラウザでサイトにアクセスすると、そのページのリクエストはナビゲーション リクエストとなり、キャッシュされたページ /app-shell.html が配信されます。(注: workbox-precaching または独自のインストール手順で、ページをキャッシュに保存する必要があります)。

デフォルトでは、すべてのナビゲーション リクエストに応答します。一部の URL にのみレスポンスを返すように制限する場合は、allowlist オプションと denylist オプションを使用して、このルートに一致するページを制限できます。

import {createHandlerBoundToURL} from 'workbox-precaching';
import {NavigationRoute, registerRoute} from 'workbox-routing';

// This assumes /app-shell.html has been precached.
const handler = createHandlerBoundToURL('/app-shell.html');
const navigationRoute = new NavigationRoute(handler, {
  allowlist: [new RegExp('/blog/')],
  denylist: [new RegExp('/blog/restricted/')],
});
registerRoute(navigationRoute);

ただし、URL が allowlistdenylist の両方にある場合、denylist が優先されます。

デフォルト ハンドラの設定

ルートに一致しないリクエストに「ハンドラ」を提供する場合は、デフォルト ハンドラを設定できます。

import {setDefaultHandler} from 'workbox-routing';

setDefaultHandler(({url, event, params}) => {
  // ...
});

キャッチ ハンドラを設定する

いずれかのルートでエラーがスローされた場合は、キャッチ ハンドラを設定することで、キャプチャとグレースフル デグラデーションを行うことができます。

import {setCatchHandler} from 'workbox-routing';

setCatchHandler(({url, event, params}) => {
  ...
});

GET 以外のリクエスト用のルートの定義

デフォルトでは、すべてのルートが GET リクエスト用のものとみなされます。

POST リクエストなど、他のリクエストをルーティングする場合は、ルートの登録時に次のようにメソッドを定義する必要があります。

import {registerRoute} from 'workbox-routing';

registerRoute(matchCb, handlerCb, 'POST');
registerRoute(new RegExp('/api/.*\\.json'), handlerCb, 'POST');

ルーターのロギング

workbox-routing のログを使用して、リクエストのフローを特定できます。このログには、Workbox で処理されている URL がハイライト表示されます。

ルーティング ログ

より詳細な情報が必要な場合は、ログレベルを debug に設定すると、Router で処理されないリクエストのログを表示できます。ログレベルの設定の詳細については、デバッグガイドをご覧ください。

デバッグ メッセージとログ ルーティング メッセージ

高度な使用方法

ワークボックス ルーターにリクエストが提供されるタイミングをより細かく制御するには、独自の Router インスタンスを作成し、ルーターを使用してリクエストに応答する必要があるときに、そのインスタンスの handleRequest() メソッドを呼び出します。

import {Router} from 'workbox-routing';

const router = new Router();

self.addEventListener('fetch', event => {
  const {request} = event;
  const responsePromise = router.handleRequest({
    event,
    request,
  });
  if (responsePromise) {
    // Router found a route to handle the request.
    event.respondWith(responsePromise);
  } else {
    // No route was found to handle the request.
  }
});

Router を直接使用する場合は、Route クラスか、いずれかの拡張クラスを使用してルートを登録する必要もあります。

import {Route, RegExpRoute, NavigationRoute, Router} from 'workbox-routing';

const router = new Router();
router.registerRoute(new Route(matchCb, handlerCb));
router.registerRoute(new RegExpRoute(new RegExp(...), handlerCb));
router.registerRoute(new NavigationRoute(handlerCb));

NavigationRoute

NavigationRoute を使用すると、ブラウザ [ナビゲーション リクエスト]https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests に一致する workbox-routing.Route を簡単に作成できます。

https://fetch.spec.whatwg.org/#concept-request-mode|modenavigate に設定されている受信リクエストにのみ一致します。

このルートは、denylist パラメータと allowlist パラメータのいずれかまたは両方を使用して、ナビゲーション リクエストのサブセットにのみ適用することもできます。

プロパティ

  • コンストラクタ

    void

    denylistallowlist の両方を指定すると、denylist が優先され、リクエストはこのルートと一致しません。

    allowlistdenylist の正規表現は、リクエストされた URL の連結された [pathname]https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname と [search]https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search の部分と照合されます。

    : これらの RegExp は、ナビゲーション中にすべてのリンク先 URL に対して評価される場合があります。複雑な RegExps は使用しないでください。ユーザーがサイトを移動する際に遅延が発生する可能性があるためです。

    constructor 関数は次のようになります。

    (handler: RouteHandler,options?: NavigationRouteMatchOptions)=> {...}

  • catchHandler
  • method

    HTTPMethod

  • setCatchHandler

    void

    setCatchHandler 関数は次のようになります。

    (handler: RouteHandler)=> {...}

    • handler

      レスポンスに解決される Promise を返すコールバック関数

NavigationRouteMatchOptions

プロパティ

  • 許可リストに掲載する

    RegExp[](省略可)

  • 拒否リスト

    RegExp[](省略可)

RegExpRoute

RegExpRoute を使用すると、正規表現ベースの workbox-routing.Route を簡単に作成できます。

同一オリジンのリクエストの場合、RegExp で一致させる必要があるのは URL の一部のみです。サードパーティのサーバーに対するリクエストでは、URL の先頭に一致する RegExp を定義する必要があります。

プロパティ

  • コンストラクタ

    void

    正規表現に [キャプチャ グループ]https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references が含まれている場合、キャプチャされた値は workbox-routing~handlerCallback params 引数に渡されます。

    constructor 関数は次のようになります。

    (regExp: RegExp,handler: RouteHandler,method?: HTTPMethod)=> {...}

    • regExp

      RegExp

      URL と照合する正規表現。

    • handler

      Response を返す Promise を返すコールバック関数。

    • method

      HTTPMethod 省略可

  • catchHandler
  • method

    HTTPMethod

  • setCatchHandler

    void

    setCatchHandler 関数は次のようになります。

    (handler: RouteHandler)=> {...}

    • handler

      レスポンスに解決される Promise を返すコールバック関数

Route

Route は、コールバック関数「match」と「handler」のペアで構成されます。「match」コールバックは、可能であれば falsy でない値を返して、リクエストを「処理」するためにルートを使用する必要があるかどうかを判断します。「handler」コールバックは、一致があると呼び出され、Response に解決される Promise を返します。

プロパティ

  • コンストラクタ

    void

    Route クラスのコンストラクタ。

    constructor 関数は次のようになります。

    (match: RouteMatchCallback,handler: RouteHandler,method?: HTTPMethod)=> {...}

    • falsy でない値を返して、経路が特定の fetch イベントに一致するかどうかを判断するコールバック関数。

    • handler

      レスポンスに解決される Promise を返すコールバック関数。

    • method

      HTTPMethod 省略可

  • catchHandler
  • method

    HTTPMethod

  • setCatchHandler

    void

    setCatchHandler 関数は次のようになります。

    (handler: RouteHandler)=> {...}

    • handler

      レスポンスに解決される Promise を返すコールバック関数

Router

Router は、1 つ以上の workbox-routing.Route を使用して FetchEvent を処理するために使用できます。一致するルートが存在する場合は、Response で応答します。

リクエストに一致するルートがない場合、Router は「default」ハンドラを使用します(定義されている場合)。

一致する Route がエラーをスローすると、Router は「catch」ハンドラを使用します(定義されている場合)。catch ハンドラは問題に適切に対処し、リクエストに応答します。

リクエストが複数のルートに一致する場合、リクエストへのレスポンスには登録されている最も早いルートが使用されます。

プロパティ

  • コンストラクタ

    void

    新しい Router を初期化します。

    constructor 関数は次のようになります。

    ()=> {...}

  • ルート

    Map<HTTPMethodRoute[]>

  • addCacheListener

    void

    ウィンドウからキャッシュに保存する URL のメッセージ イベント リスナーを追加します。これは、Service Worker が制御を開始する前にページに読み込まれたリソースをキャッシュに保存する場合に便利です。

    ウィンドウから送信されるメッセージ データの形式は次のとおりです。 ここで、urlsToCache 配列は、URL 文字列、または URL 文字列と requestInit オブジェクトの配列(fetch() に渡す場合と同じ)で構成されます。

    {
      type: 'CACHE_URLS',
      payload: {
        urlsToCache: [
          './script1.js',
          './script2.js',
          ['./script3.js', {mode: 'no-cors'}],
        ],
      },
    }
    

    addCacheListener 関数は次のようになります。

    ()=> {...}

  • addFetchListener

    void

    フェッチ イベント リスナーを追加して、ルートがイベントのリクエストと一致するとイベントに応答します。

    addFetchListener 関数は次のようになります。

    ()=> {...}

  • findMatchingRoute

    void

    リクエストと URL(必要に応じてイベント)を登録済みルートのリストと照合し、一致する場合は、対応するルートを一致によって生成されたパラメータとともに返します。

    findMatchingRoute 関数は次のようになります。

    (options: RouteMatchCallbackOptions)=> {...}

    • 戻り値

      オブジェクト

      route プロパティと params プロパティを持つオブジェクト。 一致するルートが見つかった場合は値が入力され、見つからなかった場合は undefined が設定されます。

  • handleRequest

    void

    FetchEvent オブジェクトにルーティング ルールを適用して、適切な Route のハンドラから Response を取得します。

    handleRequest 関数は次のようになります。

    (options: object)=> {...}

    • オプション

      オブジェクト

      • event

        ExtendableEvent

        リクエストをトリガーしたイベント。

      • request

        リクエスト

        処理するリクエスト。

    • 戻り値

      Promise<Response>

      登録されたルートがリクエストを処理できる場合は Promise が返されます一致するルートがなく、defaultHandler もない場合は、undefined が返されます。

  • registerRoute

    void

    ルーターにルートを登録します。

    registerRoute 関数は次のようになります。

    (route: Route)=> {...}

  • setCatchHandler

    void

    リクエストの処理中に Route がエラーをスローすると、この handler が呼び出され、レスポンスを提供する機会が与えられます。

    setCatchHandler 関数は次のようになります。

    (handler: RouteHandler)=> {...}

    • handler

      Response を返す Promise を返すコールバック関数。

  • setDefaultHandler

    void

    受信リクエストに明示的に一致するルートがない場合に呼び出されるデフォルトの handler を定義します。

    各 HTTP メソッド(GET、POST など)は、それぞれ独自のデフォルト ハンドラを取得します。

    デフォルト ハンドラがない場合、一致しないリクエストは、Service Worker が存在しないものとしてネットワークに送信されます。

    setDefaultHandler 関数は次のようになります。

    (handler: RouteHandler,method?: HTTPMethod)=> {...}

    • handler

      Response を返す Promise を返すコールバック関数。

    • method

      HTTPMethod 省略可

  • unregisterRoute

    void

    ルーターへのルートの登録を解除します。

    unregisterRoute 関数は次のようになります。

    (route: Route)=> {...}

    • 経路

      登録を解除するルート。

Methods

registerRoute()

workbox-routing.registerRoute(
  capture: string|RegExp|RouteMatchCallback|Route,
  handler?: RouteHandler,
  method?: HTTPMethod,
)

シングルトン Router インスタンスに、キャッシュ戦略を使用する RegExp、文字列、関数を簡単に登録できます。

このメソッドは、必要に応じて Route を生成し、workbox-routing.Router#registerRoute を呼び出します。

パラメータ

  • キャプチャ

    string|RegExp|RouteMatchCallbackRouteMatchCallback|Route

    キャプチャ パラメータが Route の場合、他のすべての引数は無視されます。

  • handler

    RouteHandler 省略可

  • method

    HTTPMethod 省略可

戻り値

setCatchHandler()

workbox-routing.setCatchHandler(
  handler: RouteHandler,
)

リクエストの処理中に Route がエラーをスローすると、この handler が呼び出され、レスポンスを提供する機会が与えられます。

パラメータ

  • handler

    Response を返す Promise を返すコールバック関数。

setDefaultHandler()

workbox-routing.setDefaultHandler(
  handler: RouteHandler,
)

受信リクエストに明示的に一致するルートがない場合に呼び出されるデフォルトの handler を定義します。

デフォルト ハンドラがない場合、一致しないリクエストは、Service Worker が存在しないものとしてネットワークに送信されます。

パラメータ

  • handler

    Response を返す Promise を返すコールバック関数。