はじめに
- オフラインアプリを作成するにあたりService Workerを初めて使ってみた
-
installイベントやactivateイベントの発火タイミングが少し直観的ではなく、理解を定着させるためにアウトプット
installイベントとは
ブラウザに最新のService workerがインストールされたタイミングで発火するイベントです。
Service workerをブラウザにインストールする処理は、navigator.serviceWorker.register()メソッドで行います。
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import "./index.css";
import App from "./App.tsx";
if ("serviceWorker" in navigator) {
navigator.serviceWorker.register("/sw.js");
}
createRoot(document.getElementById("root")!).render(
<StrictMode>
<App />
</StrictMode>,
);
こうすることでmain.tsxがマウントされる時にnavigator.serviceWorker.register()メソッドが実行されますが、Service Workerファイルが同じであればインストールはされません。
現在インストール済みのものと1バイトでも違いがあれば新しいService Workerと判断されます。
activateイベントとは
新しいService Workerがインストールされた後、インストール済みのService Workerが制御しているブラウザページがすべて閉じられたときに発火するイベントです。
activate状態とは、Service Workerがブラウザページを制御する準備ができた状態、または制御している状態です。
古いバージョン(インストール済みのService Worker)と新バージョンが同時にブラウザ制御をすることができません。
古いバージョンが制御しているブラウザページをすべて閉じたことが確認できてから、新しい Service Workerの仕事ができる状態になります。
ブラウザページの制御とは
「ブラウザページを制御する」とはそのページ(クライアント)から発生するイベントをService Workerがインターセプト・ハンドリングすることです。
具体的には以下のようなことが可能になります。
- fetchイベント — ページが行うネットワークリクエストを横取りして、キャッシュを返したり書き換えたりできる
- pushイベント — サーバーからのプッシュ通知を受け取れる
- syncイベント — バックグラウンド同期ができる
ページの通信や動作に介入できるイメージです。
制御下にないページのfetchリクエストはService Workerを素通りして普通にネットワークへ行きます。
ブラウザページを制御する準備ができた状態とは
上記でactivate状態の説明を次のように説明しました
activate状態とは、Service Workerがブラウザページを制御する準備ができた状態、または制御している状態です。
「ブラウザページを制御する準備ができた状態」について補足します
新しいService Workerが初めてactivate状態になった段階では、まだブラウザページを制御していません。ブラウザページを制御できるようになるのは、activate状態になった後ブラウザページをロードした後です。
流れを書くと次のような感じです。
Service Workerを更新する
↓
ブラウザページを閉じる(古いバージョンの制御が外れる)
↓
新しいブラウザページを開く(新バージョンがactivate状態になる)
↓
ブラウザページをリロードする(新バージョンがブラウザページを制御する)
activate状態には、activate(待機)状態とactivate(制御)状態の2種類がある、というイメージです。
新しいService Workerをすぐ反映させたい場合
Service Workerを更新する度にブラウザページを閉じたりするのが面倒な場合、すぐに反映させることができます。
import { precacheAndRoute } from "workbox-precaching";
declare let self: ServiceWorkerGlobalScope;
// Workboxのinjection用
precacheAndRoute(self.__WB_MANIFEST);
self.addEventListener("install", () => {
// 新しいsw.jsをすぐにアクティブ化する
self.skipWaiting();
});
self.addEventListener("activate", (event) => {
event.waitUntil(
// アクティブ化後すぐに新バージョンのService Workerでページ制御できるようにする
self.clients.claim(),
);
});
インストールしたら即座にactivate状態にする
installイベントにself.skipWaiting()メソッドを使うことで、インストールしたらすぐにactivate状態になります。
これのおかげで古いバージョンが制御しているブラウザページを閉じる必要はなくなります。
self.addEventListener("install", () => {
// 新しいsw.jsをすぐにアクティブ化する
self.skipWaiting();
});
即座にactivate(制御)状態にする
self.clients.claim()メソッドは、古いバージョンのページ制御を即座に新バージョンへ引き継いでくれるようになります。
event.waitUntil()メソッドはPromiseを引数に受け取り、そのPromiseが解決されるまでService Workerのイベント(installやactivateなど)が終了しないように延命してくれるメソッドです。
self.addEventListener("activate", (event) => {
event.waitUntil(
// アクティブ化後すぐに新バージョンのService Workerでページ制御できるようにする
self.clients.claim(),
);
});
おわりに
開発中にService workerが反映されずに、問題の切り分けに時間がかかった場面がありました。
整理すると各イベントとブラウザページの制御の関係性がはっきり見えたので、fetchイベントやpushイベントなどインターセプトする処理の実装に注力できそうです。
参考
https://developer.mozilla.org/ja/docs/Web/API/Service_Worker_API/Using_Service_Workers
https://developer.mozilla.org/ja/docs/Web/API/ServiceWorkerContainer/register
JISOUのメンバー募集中!
プログラミングコーチングJISOUでは、新たなメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか?
興味のある方は、ぜひホームページをのぞいてみてください!
▼▼▼