ServiceWorkerをproductionで使ってる話
@shokai
右のメニューの Start presentation
でスライドになります
画面遷移が速くなった
ScrapboxはWiKiみたいなノートアプリです
今日の話
1. ServiceWorkerって何?
2. productionで使って得た知見
ServiceWorkerって何?
わかる人いますか
私もよくわかりませんが…
ググるとわかる
> リッチなオフライン体験、定期的なバックグラウンド同期、プッシュ通知など、これまでネイティブアプリを必要としていた機能が Web にもやってきます。Service Worker はそれらの機能を提供する基盤技術です。
回線切ってググるとわかる
Chromeのホーム画面はServiceWorkerで実装されてるから、オフラインでも表示できる
workerのソースも見れるぞ
オフライン表示だけではない
webページからRequestが発行されて
ServiceWorkerを通って
サーバーに送信される
サーバーからResponseが返ってきて
ServiceWorkerを通って
webページにデータが戻る
ブラウザ内で動くプログラマブルなproxy、というイメージ
色々できる
RequestやResponseを途中で書き換えたり
勝手にRequestを発行してcacheを温めておいたり
プログラマブルなproxy
オフライン表示機能がServiceWorkerについてるわけではない
がんばって自分で実装する
fetch event
等を使う
オフライン表示の例
cache hitすれば、Requestをサーバーに送らずUIスレッドに返す
cache hitしなければ、サーバーからfetchして返す
sw.jsself.addEventListener('fetch', (event) => {
const {request} = event
if (request.method !== 'GET') return // GET以外をcacheするの怖い
event.respondWith((async () => {
// cacheがあれば返す
const cachedResponse = await self.caches.match(request)
if (cachedResponse) return cachedResponse
// cacheが無い場合
const response = await fetch(request)
const cache = await self.caches.open('foo') // 名前は何でも良し
cache.put(request, response)
return response
})())
})
self.
とは
ServiceWorkerにおける window.
や global.
のようなグローバルオブジェクト
CacheStorageとは
KVS
key: Request
value: Response
cache.match(request)
で探せる
ServiceWorker内では self.caches
window.caches
と共通
cache.addAll([...urls])
で一気にfetchする事もできる
UIスレッド側でやる事
ServiceWorker自体のインストールだけやる
navigator.serviceWorker.register('./sw.js', {scope: '/'})
このRequestはServiceWorkerにハンドルしてもらって、こっちはしないで〜
とかはできない
全てのRequestがServiceWorkerを通ってしまう
webページ内に表示している別originへのリクエストも含む
Responseがどこから来た物なのか判別できない
cache storageから来たのか?
ServiceWorkerはちゃんと最新データをfetchしたのか?
ServiceWorkerがHTTP headerを追加する等、各自で工夫が必要
光速を超えた
サーバーはアメリカにあるのに、clickした瞬間に画面が表示される
アドレスバーが書き換わった直後にUIスレッドはfetchしている
そこからパケットが太平洋を往復する間、待つ必要があった
click前にcacheを温めておいてもらうようにした
Random jumpも速い
ランダムにページを表示するボタン
インターンに来ている
が一瞬で作ってくれた
ランダムなのに速い
ランダム表示したら、次にランダム表示する予定のページをprefetchしている
対応ブラウザ
IE以外の最新版ならok
window閉じても生きてる
たぶんブラウザのプロセスが生きてる限り生きてるはず
setInterval
で定期的にcacheを更新なんて事も可能
でもたまに勝手に死んでる気がする
どうやって観測すればいいんだ?ぜんぜんわからない
ライフサイクル
ServiceWorkerが有効になるまで
1. Download
2. Install
3. Activate
ServiceWorkerのインストール自体を、UIスレッドのJavaScriptで行わなければならない
navigator.serviceWorker.register('./sw.js', {scope: '/'})
最初にアクセスしてactivateされるまでは、ServiceWorkerが存在しない
ダウンロード失敗してインストールできない事もある
ServiceWorkerとclient jsやサーバーAPIのバージョンがズレてるタイミングが必ず存在する
アクセス毎に register
再実行する事で更新する為
こわい
ServiceWorkerにstateを持たせたりすると絶対こわい
24時間毎に自動更新される
らしい
毎日開発してるので、自動更新を観測する暇がない…
ぜんぜんわからない
Request/Response以外にも
push通知
右上に出るやつ
オフラインの時に発行されたRequestをとっておいて、オンラインになったら一気に送信してくれる
らしい(まだ試したことない)
UIスレッドと通信
色々できてすごい!!
productionで使って得た知見
インストールするユーザーを絞った
まず最新のChromeを使っているログインユーザーだけ
理由
ServiceWorkerに不具合があった場合に、ServiceWorkerの更新ができなくなる場合がある
インストールはUIスレッドのJavaScriptから行うので
index.js
もServiceWorkerを通ってUIスレッドに配信される
24時間毎の自動更新もあるが、丸一日使えないと困る
ServiceWorker内のエラーを全部捕捉できる
2017年12月にSentryのclientライブラリがfetchに対応した
TypeError
がいっぱいでる
fetch結果がstatus 200系以外だとでる
ユーザーが埋め込んだ画像とか
名前が変で混乱した
原因:try-catch忘れてた
PrefetchのせいでRequest増えた
今の所は元気です
回線速度を計測できる
navigator.connection.rtt
でレイテンシを計測したかった
Chromeにしか実装されてない
fetchの前と後をServiceWorkerでフックして、時間をはかる
遅い回線ではprefetchをやめる等
SPAの初期表示に必要な部品だけcacheしてみた
HTML/JS/CSS/font/画像だけ
APIはcacheしない
サーバーに飛ぶRequest数が減る
304 not modifiedより速い
3G環境では初期ロードが1.5秒速くなった
レイテンシが小さい環境ではあまり変化なし
public WiFiのログイン画面がハンドルできない
自分がなぜサーバーに接続できないのか知る手段が無い
ぜんぜんわからない
スマホで出るやつをwebサイトでもやる方法を誰か教えてくれ
private windowでもRequestがServiceWorker通ってる
Cache Storageは別になっているようだが
普通のwindowの方でインストールされたら、private windowでは既にインストールされた事になっているようだ
まとめ
ただのオフライン表示システムではない
プログラマブルなproxyです
下手すると24時間サービスが開けなくなる恐怖
わからない事だらけだ
怖いのでSentry入れよう