はじめに
TIGの川端です。
先日、Vue.js + Service Worker開発案件が終わりました。その振り返りとして、Service Worker開発で起きた不具合と対応方法を記載します。
Service Workerとは
Service Workerは、ブラウザがWebページとは別にバックグラウンドで実行するJavaScriptになります。
利用ブラウザ/バージョン情報など
- Chrome v88.0.4324.146
- Vue.js v2.6.11
起きた不具合その1
事象
Service Worker上で、setInterval
の処理を用意したところ、数分で止まるという報告が上がりました。setInterval
は、バックグラウンド上で定期的にある処理をするために用意したものです。
const timer = setInterval(() => { |
原因
調べてみるとService Workerの活動には制限があるようでした。
またブラウザのDevToolを起動している場合は、Service Workerは常時活動中となり、setInterval
の処理が止まることはありません。開発中はブラウザのDevToolを常時起動中であったため、本件不具合に気づかないという事態になりました…
対応
setInterval
の処理をService WorkerからVue.js側(Webアプリ側)に移動しました。
簡単な例ですが、下記のように実行したい画面のComponentに組み込みました。
<template> |
補足
本記事は、setInterval
に焦点を当てましたが、Service Worker側に用意したWebSocket受信処理も止まってしまったため、WebSocket受信処理もVue.js側に移動する対応も実施しました。
起きた不具合その2
事象
[Ctrl]+[Shift]+[R]キーでリロードすると、下記のエラーが出てVue.jsからService Workerへのメッセージ送信が失敗するという事象が起きました。
Uncaught TypeError: Cannot read property 'postMessage' of null |
原因
[Ctrl]+[Shift]+[R]キーでリロードすると、Service Workerが解除され、下記のcontroller
がnull
になったことが原因でした。
navigator.serviceWorker.controller.postMessage({ msg }) |
Service Worker Controllerを確認すると、
navigator.serviceWorker.controller returns null if the request is a force refresh (shift+refresh).
の記載があり、[Ctrl]+[Shift]+[R]キーでリロードしたときにcontroller
がnull
になるのは仕様でした。
対応
再度Service WorkerがWebアプリをコントロールする状態になるように下記を実施しました。
まずVue.js側に、Service Workerがactive
になったら、Service Worker側にclaim
するようにメッセージを送ります。
if ('serviceWorker' in navigator) { |
次にService Worker側で該当のメッセージを受け取ったら、self.clients.claim()
を実施します。
self.onmessage = (message) => { |
ここまで対応すると、Service WorkerがWebアプリをコントロールしている状態になります。
またService Workerがコントロールする状態になるまで、navigator.serviceWorker.controller.postMessage
の処理は失敗します。
その失敗した処理のリカバリ方法として、下記のように画面をリロードして再実行するように対応しました。
if ('serviceWorker' in navigator) { |
所感
Service Worker開発で起きた不具合を2例紹介しました。
なかなか解決策が見つからず辛いと感じることもありましたが、こうして考えた解決策を公開できて、大変嬉しく思っています。