PWA: Workbox Routing 和訳 (その1)


Workbox Routing

参照元ページ: https://developers.google.com/web/tools/workbox/modules/workbox-routing

Workboxルーティングとは?

サービスワーカーはページに対するネットワークリクエストをインターセプト、つまり横取りできます。それによってブラウザへ返されるコンテンツは、キャッシュ経由、ネットワーク経由もしくはサービスワーカーが生成したものなど多岐に渡ります。

workbox-routingを使うことで、リクエストに対する様々なレスポンスを返すためのルーティングの設定を簡単に行うことができます。

ルーティングがどのように行われるか

ネットワークへのリクエストがサービスワーカーのfetchイベントを発火させる時、workbox-routingは与えらたルートとハンドラーを使ってリクエストに対するレスポンスを返します。

上記の図で大切なことは:

  • リクエストメソッドは重要です。デフォルトではルーティングは"GET"メソッドで指定されています。もし他のメソッドをインターセプトする場合は、明示的に渡してあげる必要があります。
  • ルーティング登録の順番は大切です。一つにリクエストに対して複数のルーティングが設定されている場合は、一番最初に登録されているものがリクエストに対して有効になります。(補足: 上図で"Was there a matching Route?"で最初にマッチしたものが使用されるということですね)

ルーティングを登録するには、コールバック、正規表現、Routeインスタンスで行います。

ルートのマッチングとハンドリング

workboxのルーティングは二つの機能のみから成ります。
- マッチング: ルートがリクエストと一致しているかを決める
- ハンドリング: リクエストに対してResponseでレスポンスを返す

Workboxはマッチングとハンドリングを行うヘルパー関数を持っていますが、もしそれとは異なる挙動を期待したい時はカスタムのマッチングとハンドリング関数を書くのがベストです。

マッチング関数はFetchEventURLオブジェクトを元にリクエストとマッチしているか否かの値を返します。簡単な例ですと、下記のように特定のURLとマッチさせることができます。


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

ほとんどのユースケースはurlevent.requestRequestとマッチしているかを調べることでカバーされます。

ハンドラーでは与えられたurleventを元にどのようにレスポンスを返すかを決めることになります。それはネットワーク経由かキャッシュ経由、サービスワーカー(が生成したコンテンツ)経由かもしれません。


const handlerCb = ({url, event, params}) => {
  return fetch(event.request)
    .then((response) => {
      return response.text()
    })
    .then((responseBody) => {
      return new Response(`${responseBody} 例えばここにコンテンツを追加できます`)
    })
}

ハンドラーはresolveされたタイミングでResponseを返すPromiseをリターンしなくはいけません。paramsの値はmatch関数の戻り値です。

これらのコールバックは次のように記述します。


workbox.routing.registerRoute(matchCb, handelrCb);

一つ注意しなくはいけないのは"match"関数は同期的に値を返すため、非同期では使えません。なぜなら、Routerfetchイベントやfetchが失敗した場合のほかのfetchイベントに同期的にレスポンスを返す必要があるからです。

通常"handler"関数はworkbox-strategiesで提供されているストラテジーを使います。


workbox.routing.regosterRoute(
  matchCb,
  new workbox.strategies.StaleWhileRevalidate()
);

ルーティングの正規表現の書き方

大抵のケースでは"match"の代わりに正規表現を使います。Workboxによってこの部分を簡単に書くことができます。


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

同一オリジンからのリクエストには、正規表現の適用が緩くなるため上記の書き方は次の3パターンでマッチします。

しかし、クロスオリジンの場合はURLの先頭から一致する必要があります。想定外にサードパーティのアセットにマッチしてしまうことを防ぐためです。

そのためクロスオリジンのリクエストに対しては
new RegExp('https://cdn.third-party-site.com.*/styles/.*\.css')のようにフルパスで記述します。