Service Worker の人生

Service Worker のライフサイクルを理解せずに、Service Worker が何をしているのか把握するのは困難です。 その内部の仕組みは、不明瞭、さらには任意に見えるでしょう。 他のブラウザ API と同様に、Service Worker の動作は明確に定義されており、 オフライン アプリケーションを可能にするには、 ユーザー エクスペリエンスを損なうことなく更新を容易にします。

ワークボックスの説明に入る前に Workbox の仕組みを理解するために、Service Worker のライフサイクルを理解することが重要です。

用語の定義

Service Worker のライフサイクルに入る前に ライフサイクルの動作に関する用語を定義することに価値があります。

コントロールとスコープ

Service Worker の動作を理解するには、制御という考え方が不可欠です。 Service Worker によって制御されているページとは、Service Worker がネットワーク リクエストをインターセプトできるページのことです。 Service Worker が存在し、指定されたスコープ内でページを処理できます。

範囲

Service Worker のスコープは、ウェブサーバー上のその位置によって決まります。 Service Worker が /subdir/index.html のページで実行され、/subdir/sw.js にある場合、 Service Worker のスコープは /subdir/ です。 スコープのコンセプトの実例を確認するには、次の例をご覧ください。

  1. 次に移動: https://service-worker-scope-viewer.glitch.me/subdir/index.html. ページを制御している Service Worker がないことを示すメッセージが表示されます。 ただし、そのページでは https://service-worker-scope-viewer.glitch.me/subdir/sw.js から Service Worker が登録されます。
  2. ページを再読み込みします。Service Worker が登録され、アクティブになったため、 ページを制御しているからです Service Worker のスコープを含むフォーム その URL が表示されます。 注: ページの再読み込みはスコープとは関係ありませんが、 Service Worker のライフサイクルについて学びましょう。
  3. 次に、https://service-worker-scope-viewer.glitch.me/index.html に移動します。 Service Worker はこのオリジンに登録されていますが、 現在の Service Worker がないことを示すメッセージが表示されます。 これは、このページが登録済みの Service Worker のスコープ内にないためです。

スコープは、Service Worker が制御するページを制限します。 この例の場合、/subdir/sw.js から読み込まれた Service Worker は、/subdir/ またはそのサブツリーにあるページのみを制御できます。

以上がデフォルトのスコープ設定です。 ただし、スコープの最大値をオーバーライドするには、 Service-Worker-Allowed レスポンス ヘッダー、 渡すだけです。 register メソッドの scope オプション

Service Worker のスコープを送信元のサブセットに限定する正当な理由がない限り、 ウェブサーバーのルート ディレクトリから Service Worker を読み込み、スコープをできるだけ広げます。 Service-Worker-Allowed ヘッダーは気にしないでください。その方がずっとシンプルです。

クライアント

Service Worker がページを制御していると言えば、実際にはクライアントを制御しています。 「クライアント」とは、開いているページのうち、URL が Service Worker の範囲内にあるものを指します。 具体的には、これらは WindowClient のインスタンスです。

新しい Service Worker のライフサイクル

Service Worker でページを制御するには まず存在にしなければなりません まず、アクティブな Service Worker がないウェブサイトにまったく新しい Service Worker をデプロイした場合、どうなるでしょうか。

登録

登録は、Service Worker ライフサイクルの最初のステップです。

<!-- In index.html, for example: -->
<script>
  // Don't register the service worker
  // until the page has fully loaded
  window.addEventListener('load', () => {
    // Is service worker available?
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/sw.js').then(() => {
        console.log('Service worker registered!');
      }).catch((error) => {
        console.warn('Error registering service worker:');
        console.warn(error);
      });
    }
  });
</script>

このコードはメインスレッドで実行され、次の処理を行います。

  1. ウェブサイトへのユーザーの初回アクセスは、登録済みの Service Worker なしで行われるため、 ページが完全に読み込まれるまで待ってから、ページを登録します。 これにより、Service Worker が何かを事前キャッシュした場合でも、帯域幅の競合を回避できます。
  2. Service Worker は十分にサポートされていますが、 サポートされていないブラウザでのエラーを防ぐには、クイック チェックを行います。
  3. ページが完全に読み込まれ、Service Worker がサポートされている場合は、/sw.js を登録します。

理解しておくべき重要な点は以下のとおりです。

  • Service Worker は、 HTTPS または localhost 経由でのみ利用可能です。
  • Service Worker の内容に構文エラーがある場合、 登録が失敗し、Service Worker は破棄されます。
  • 注意: Service Worker はスコープ内で動作します。 ここでは、ルート ディレクトリから読み込まれたので、スコープはオリジン全体です。
  • 登録が開始されると、Service Worker の状態は 'installing' に設定されます。

登録が完了すると、インストールが開始されます。

インストール

Service Worker は Service Worker で 登録後に install イベントが作成されます。 install は Service Worker ごとに 1 回だけ呼び出され、更新されるまで再度呼び出されません。 install イベントのコールバックは、addEventListener でワーカーのスコープに登録できます。

// /sw.js
self.addEventListener('install', (event) => {
  const cacheKey = 'MyFancyCacheName_v1';

  event.waitUntil(caches.open(cacheKey).then((cache) => {
    // Add all the assets in the array to the 'MyFancyCacheName_v1'
    // `Cache` instance for later use.
    return cache.addAll([
      '/css/global.bc7b80b7.css',
      '/css/home.fe5d0b23.css',
      '/js/home.d3cc4ba4.js',
      '/js/jquery.43ca4933.js'
    ]);
  }));
});

これにより、新しい Cache インスタンスが作成され、アセットが事前キャッシュに保存されます。 後でプレキャッシュについて話す機会はたくさんありますが、 Chronicle SOAR における event.waitUntilevent.waitUntil は Promise を受け入れます。 その Promise が解決されるまで待機します。 この例では、Promise は 2 つの非同期処理を行います。

  1. 'MyFancyCache_v1' という名前の新しい Cache インスタンスを作成します。
  2. キャッシュの作成後 アセット URL の配列は、非同期リクエストを使用して addAll メソッド

event.waitUntil に渡された Promise が次である場合、インストールは失敗します。 否承認となります。 この場合、Service Worker は破棄されます。

Promise が解決する場合、 インストールに成功し、Service Worker の状態が 'installed' に変わり、アクティブになります。

有効化

登録とインストールが成功したら Service Worker が有効になり、状態が 'activating' になる 処理は、Service Worker の activate イベント。 この場合の一般的なタスクは、古いキャッシュの 新しい Service Worker の場合は 今のところ関係ないので、 Service Worker のアップデートについて触れる際に、これを拡張する予定です。

新しい Service Worker の場合、install が成功した直後に activate が起動します。 有効化が完了したら Service Worker の状態は 'activated' になります。 デフォルトでは 新しい Service Worker は、次のナビゲーションまたはページが更新されるまでページの制御を開始しません。

Service Worker の更新の処理

最初の Service Worker がデプロイされると、 後で更新する必要があります たとえば、リクエスト処理またはプリキャッシュ ロジックで変更が発生した場合、更新が必要になることがあります。

更新が行われるタイミング

ブラウザは、次の場合に Service Worker の更新を確認します。

  • ユーザーが Service Worker のスコープ内のページに移動します。
  • navigator.serviceWorker.register() 現在インストールされている Service Worker とは異なる URL で呼び出されますが、Service Worker の URL は変更しないでください
  • navigator.serviceWorker.register() インストールされている Service Worker と同じ URL で呼び出される スコープは異なります 繰り返しになりますが、可能であれば、スコープをオリジンのルートに保持することで、これを回避できます。
  • 'push''sync' などのイベントが アラートは過去 24 時間以内にトリガーされました。ただし、これらのイベントはまだ気にしないでください。

更新が行われる仕組み

ブラウザが Service Worker をいつ更新するのかを知ることは重要です。 「どのように」も重要です。Service Worker の URL またはスコープは変わらないと仮定すると、 現在インストールされている Service Worker は、内容が変更された場合にのみ新しいバージョンに更新されます。

ブラウザは、いくつかの方法で変更を検出します。

  • Compute Engine が要求したスクリプトに対するバイト単位の変更 importScripts(該当する場合)。
  • Service Worker のトップレベルのコードが変更された場合、 これは、ブラウザが生成するフィンガープリントに影響します。

この場合、ブラウザは多くの複雑な作業を行います。 ブラウザが Service Worker の内容の変更を確実に検出するために必要なすべてのものを確実に備えるため、 HTTP キャッシュに保持を指示したり、ファイル名を変更したりしません。 Service Worker のスコープ内に新しいページに移動すると、ブラウザは自動的に更新チェックを実行します。

更新チェックを手動でトリガーする

更新に関しては、通常、登録ロジックは変更されません。 ただし、例外として、ウェブサイトのセッションが長期間継続する場合などがあります。 これはシングルページ アプリケーションで発生し、 ナビゲーション リクエストが発生することは稀ですが、 アプリケーションでは通常、アプリケーションのライフサイクルの開始時に 1 つのナビゲーション リクエストが発生するためです。 そのような状況では、メインスレッドで手動更新をトリガーできます。

navigator.serviceWorker.ready.then((registration) => {
  registration.update();
});

従来のウェブサイトでは ユーザーセッションが長く続かない場合や 手動更新のトリガーは不要です

インストール

バンドラを使用して静的アセットを生成する場合は、 それらのアセットの名前にハッシュが含まれ framework.3defa9d2.js など。 後でオフラインでアクセスできるように、一部のアセットが事前キャッシュされているとします。 この場合、更新されたアセットを事前キャッシュに保存するには、Service Worker を更新する必要があります。

self.addEventListener('install', (event) => {
  const cacheKey = 'MyFancyCacheName_v2';

  event.waitUntil(caches.open(cacheKey).then((cache) => {
    // Add all the assets in the array to the 'MyFancyCacheName_v2'
    // `Cache` instance for later use.
    return cache.addAll([
      '/css/global.ced4aef2.css',
      '/css/home.cbe409ad.css',
      '/js/home.109defa4.js',
      '/js/jquery.38caf32d.js'
    ]);
  }));
});

前述の最初の install イベントの例とは、次の 2 つの点が異なります。

  1. キーが 'MyFancyCacheName_v2' の新しい Cache インスタンスが作成されます。
  2. 事前キャッシュに保存されたアセット名が変更されました。
で確認できます。

注意すべき点として、更新された Service Worker が以前の Service Worker とともにインストールされます。 つまり、古い Service Worker は、開いているページを制御し、インストール後も 新しいカードを有効化するまで待機状態になります。

デフォルトでは、古い Service Worker がクライアントを制御していない場合、新しい Service Worker が有効になります。 これは、関連するウェブサイトで開いているタブがすべて閉じられた場合に発生します。

有効化

更新された Service Worker がインストールされ、待機フェーズが終了すると、 アクティブになり古い Service Worker は破棄されます。 更新された Service Worker の activate イベントでの一般的なタスクは、古いキャッシュのプルーニングです。 次を使用して開いているすべての Cache インスタンスのキーを取得し、古いキャッシュを削除します。 caches.keys 定義された許可リストに含まれていないキャッシュを caches.delete:

self.addEventListener('activate', (event) => {
  // Specify allowed cache keys
  const cacheAllowList = ['MyFancyCacheName_v2'];

  // Get all the currently active `Cache` instances.
  event.waitUntil(caches.keys().then((keys) => {
    // Delete all caches that aren't in the allow list:
    return Promise.all(keys.map((key) => {
      if (!cacheAllowList.includes(key)) {
        return caches.delete(key);
      }
    }));
  }));
});

古いキャッシュは自身を整理しません。 自分たちでやらないといけないかもしれないし、 保存容量。 最初の Service Worker の 'MyFancyCacheName_v1' は古いため、 キャッシュ許可リストが更新され、'MyFancyCacheName_v2' を指定する。 異なる名前のキャッシュが削除されます。

古いキャッシュが削除されると、activate イベントは終了します。 この時点で新しい Service Worker がページを制御し、 ようやく古いものと交換するんだ!

ライフサイクルはずっと続く

Service Worker のデプロイと更新の処理に Workbox を使用するかどうか Service Worker API を直接使用する場合、 Service Worker のライフサイクルを把握することは重要です。 このような点を理解すれば、Service Worker の動作は不思議ではなく、もっと論理的に見えるはずです。

さらに詳しく知りたい方のために Chronicle のさまざまな機能を この記事(著者: Jake Archibald) サービスのライフサイクル全体の仕組みは微妙に異なりますが、 知識は豊富です。その知識は Workbox を使用する際に非常に役立ちます。