Service Workerのライフサイクルをちゃんと理解する

PWA(Progressive Web App)で使われている「Service Worker」について
動きを正しく理解するために処理の流れを追っていきます。

主に以下の内容を見ながら学んでいきます。
https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle?hl=ja

↑を検証用に少し手を加えてます。
https://github.com/morix1500/service_worker_test

最初のService Worker

まだService Workerが登録されていないときの動きを追っていきます。

https://github.com/morix1500/service_worker_test/tree/master/v1

ブラウザで http://localhost/v1/in-scope/ を閲覧します。

# consoleログ

SW registered!
ServiceWorkerRegistration {installing: ServiceWorker, waiting: null, active: null, navigationPreload: NavigationPreloadManager, scope: "http://localhost/v1/in-scope/", …}
V1 installing…
V1 now ready to handle fetches!

# この時、ブラウザでは「man.png」が表示された。

つまり最初にService Workerが登録されるときは

  • まずregisterイベントが走り、Service Workerが登録される
  • 次にService Workerのinstallイベントが走る
  • 最後にService Workerのactivateイベントが走る
  • Service Workerのfetchイベントは走らない

というのがわかりました。

次にページを更新してみたときのconsoleログを見てみます

# consoleログ

SW registered!
ServiceWorkerRegistration {installing: null, waiting: null, active: ServiceWorker, navigationPreload: NavigationPreloadManager, scope: "http://localhost/v1/in-scope/", …}
URL {href: "http://localhost/v1/in-scope/man.png", origin: "http://localhost", protocol: "http:", username: "", password: "", …}

# この時、ブラウザでは「woman.png」が表示された。

Service Worker登録後にページを更新してみると

  • registerイベントが走る
  • Service Workerのfetchイベントが走る
  • fetch内のevent.respondWith が実行される
  • Service Workerのinstallとactivateイベントは走らない

さて、Service WorkerのScopeは以下のようになってます。

// /v1/in-scope/index.html
navigator.serviceWorker.register('/v1/sw.js', {scope: '/v1/in-scope/'})

スコープを /v1/in-scope/ にしてます。
ここで http://localhost/v1/out-scope を見てみます。

# consoleログ

# Service Worker登録
SW registered!
ServiceWorkerRegistration {installing: ServiceWorker, waiting: null, active: null, navigationPreload: NavigationPreloadManager, scope: "http://localhost/v1/in-scope/", …}
V1 installing…
V1 now ready to handle fetches!

# ページ更新
SW registered!
ServiceWorkerRegistration {installing: null, waiting: null, active: ServiceWorker, navigationPreload: NavigationPreloadManager, scope: "http://localhost/v1/in-scope/", …}

このことからわかるのは

  • スコープを /v1/in-scope/ にしてるので、スコープ外のものはfetchイベントが走らない

ということです。

Service Workerのアップデート

続いてService Workerのアップデート時の挙動を見てみます。
確認手順としては

  1. 上記のv1のソースをv2としてコピー
  2. v2のソースで「v1」となっているところを「v2」に置換
  3. http://localhost/v2/in-scope/ を開く
  4. woman.png が表示されているのを確認する
  5. sw.jsを https://github.com/morix1500/service_worker_test/blob/master/v2/sw.js にする

v2のソースは以下です。
https://github.com/morix1500/service_worker_test/tree/master/v2

# Consoleログ

SW registered!
ServiceWorkerRegistration {installing: null, waiting: null, active: ServiceWorker, navigationPreload: NavigationPreloadManager, scope: "http://localhost/v2/in-scope/", …}
V2 installing…

# この時ブラウザではwoman.pngが表示される

Service WorkerのStatusは以下のようになっていました。

その後、ページを更新してもwoman.png が出続け、旧Service Workerで表示されていたConsoleログが表示されました。

これまででわかること

  • Service Workerが更新されるとService Workerのinstallイベントが走る
  • ブラウザ画面上の挙動は旧Service Workerの動作になる
  • ページ更新だけだと新Service WorkerのActivateイベントが走らない

ではこの状態でブラウザのタブを閉じ、開きなおします。

# Consoleログ

V2 installing…
delete chache: static-v1
V2 now ready to handle fetches!
URL
SW registered!
ServiceWorkerRegistration {installing: null, waiting: null, active: ServiceWorker, navigationPreload: NavigationPreloadManager, scope: "http://localhost/v2/in-scope/", …}
URL {href: "http://localhost/v2/in-scope/man.png", origin: "http://localhost", protocol: "http:", username: "", password: "", …}

# この時ブラウザではgohan.pngが表示された

このことからわかるのは

  • タブの開きなおしで新しいService Workerが動く

またテクニックとして、キャッシュのバージョニングを行っています。
v1では「static-v1」というキャッシュを作成し、そこにwoman.pngのキャッシュを追加してました。
Service Workerのバージョンアップをする場合、キャッシュを切り替える必要があるので
「static-v2」というキャッシュ名に変更しています。
バージョンアップ時に旧バージョンのキャッシュを削除するようにしています。

最後に

ふわっと理解していたService Workerのライフサイクルをちゃんと動きを追って理解できました!