ServiceWorkerの記事を書いた&所感

HTML5 Experts.jpにServiceWorkerの記事を書いた。

Google I/Oで見たServiceWorkerのセッションを紹介したもの。セッションの内容を文字にして説明しているという雑な記事。セッションのビデオを見るよりは早いかと思うので、見てない人にはよいかと。見ている人は…なんだろ、紹介されたコードにちょっとコメント入れたので、それを……

コードで思い出した。サンプルで&&になってしまってるんだけど、なんかWordPressプラグインのせいなのかどうしても直せなかった。Syntax Highlighting的なプラグインもよくわからず使えず…WP力が低い。

記事を書いていていろいろ考えたことがあったので以下雑多に後記てきなものをば。

ServiceWorkerの更新

記事でも書いたけども、そんな触れられてなかったSWの更新についてちょっとだけ広げる。

AppCacheの「セットされたら最後」問題をアドレスするため、ServiceWorkerではブラウザが最低でも24時間に一度はSWの更新チェックをすることになっている。FetchしてみてSWが更新されていれば、そのSWのoninstallハンドラを実行する。だいたいキャッシュの追加とかになると思う。

oninstallハンドラが処理されたあとは待機状態になる。待機する理由は前のバージョンのSWが実行されているタブがあったりすると問題だから。前のバージョンがコントロールしているアプリがすべて終了したら新しいSWが起動して、activateイベントが発火する。なのでSWにonactivateハンドラを書いとけばそれが処理される。activatefetchが発火する前に必ず発火するようになってるので、破壊的?な処理はここですることになると。

待機せずに新しいSWをすぐ使わせたい場合は、oninstallハンドラ内でe.replace()を実行すると入れ替わってくれる。キャッシュに追加し忘れたものを追加したとか、そういう小さい&致命的なfailureが起こらない場合はこれを使ってもいいのかなと。

ちょっと気になってるのが、v2が待機状態になる前にoninstallが走ること。ちゃんと仕様読んでないだけなんだけど、以下の場合どうなるのかなと。

  1. v1インストールで https://example.com/foo.png をキャッシュ
  2. v2開発。foo.png がちょう変わる
  3. v2インストールでその新しい foo.png をキャッシュ
  4. 開きっぱのタブがあってv2が待機状態に
  5. foo.pngへのfetchが行われる

こうなったとき、v2のキャッシュ参照されたりしないのかなと。ちゃんと仕様読もうか……

named Cache

セッションや紹介記事とかで、最初のinstall時にキャッシュに“static-v1”という名前をつけて静的リソースをキャッシュさせている。

this.oninstall = function (e) {
  e.waitUntil(
    caches.create('static-v1').then(function (cache) {
      return cache.add(
        '/', // ...
      )
    })
  )
}

Cacheオブジェクトは自動では消えないのでかなり面白いんだけど、この「自動で消えないものに名前を付けられる」というところで、特定の特定のnamed cache前提のコードを書いてしまうケースがあるのではないかとちょっと思った。

// v1
this.oninstall = function (e) {
  e.waitUntil(
    caches.create('static-images').then(function (cache) {
      return cache.add(
        '/img/...',
        '/img/icons/...',
        // ...

v1時にこういう画像のキャッシュを用意したとする。その後バージョンを上げるんだけど、使ってる画像は一緒ということで、常に「static-images」なCacheを参照するようにしちゃう。

// v2
this.onfetch = function (e) {
  if (e.request.context === 'image') {
    e.respondWith(
      caches.get('static-images').then(function(responses) {
        return responses[0]
      }
    ).catch(function () {
      return e.default()
    })
  }
}

で、「画像変えてないのにインストール時にFetch走るとかやだよね、消そっか」みたいなノリでoninstall内の caches.create('static-images') なところを消してしまった。これで何が起こるかというと、v2以降に始めてアクセスした人には「static-images」なキャッシュが作られないので、悲しくなると。

単なる思考実験ではあるんだけど、

  • Webという「インストール」の概念がなかったものにそれがもたらされる
  • 自動では消えないキャッシュを用意でき、さらに参照しやすい名前までつけられる

というのがあるので、最初から継続的に開発しているひとだと変にこういう状態に陥ったりしないかなと。

開発ツール側にもSWにくっついたCacheを削除するとかそういうオプションは要るだろうなあ。

Fetchのrequest context

SWのセッションを見てておもしろかったのが、テンプレート適用やら画像のトランスコードやら、オフラインに限らないプロキシ的な使い方をするところ。

画像のトランスコードなんかは、フォーマットに対応してないブラウザでの表示だけじゃなく、対応してるブラウザで画像をダウンロードさせたときにも使えるかなと思った。ほら、WebPファイルダウンロードできても困るケースはけっこうあると思うので。

Fetch仕様ではrequest contextにdownloadなんてものがある。<a download href='blah'>なリソースのfetchはそんなコンテキストらしい。これがSWでも取得できたら画像をURI化したものをhrefにぶっこんどいて、onfetch内でひっかけられたらなと。

<a href="foo.webp" download>donwload image</a>
this.onfetch = function (e) {
  if (e.request.context === 'donwload') {
    e.respondWith(
      e.default().then(function (r) {
        if (r.headers.get('content-type') === 'image/webp') {
          return transcode(r.body);
        } else {
          return r;
        }
      })
    )
  }
})

こんなことができるとなあと思ってたんだけど、FetchEvent.contextにはdownloadがなかった…うおお。