オフラインでも動作するWebアプリのために。
成果物
情報源
ServiceWorker
ServiceWorkerとは、プロキシサーバのように振る舞う。リソースへのリクエストを横取り(hook)して任意の処理を行う。ふつうはCache APIを用いてリソースをキャッシュ(ローカルに保存)する。これにてネットが使えないオフライン環境でも閲覧できる。オフライン時はキャッシュを使うことで。
実装
- ServiceWorkerのイベント処理を定義したjsファイルを登録する:
navigator.serviceWorker.register()
- 1のファイルでイベント処理を定義する: イベントハンドラ:
install
,fetch
,activate
イベント | 概要 | 役割 |
---|---|---|
install |
初回に行われる。2回目以降は実行されない。 | 初回時すべてをキャッシュする。 |
fetch |
ファイル単位で処理できる | キャッシュになければ追加する。 |
activate |
有効化。 | 完了時の処理 |
コード例
ファイル | 役割 |
---|---|
index.html | main.jsを呼び出す。 |
main.js | ServiceWorkerTest.jsを呼び出す。 |
ServiceWorkerTest.js | sw.jsを登録する。 |
sw.js | ServiceWorkerのイベント処理を実装する。 |
index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>JSのServiceWorkerを使う。</title> <script src="./main.js" type="module"></script> </head> <body> <h1>JSのServiceWorkerを使う。</h1> <p id="kind"></p> </body> </html>
main.js
import ServiceWorkerTest from './ServiceWorkerTest.js'; window.addEventListener('load', (event) => { let test = new ServiceWorkerTest(); });
ServiceWorkerTest.js
export default class ServiceWorkerTest { constructor() { this.#setup(); } #setup() { console.log('has navigator:',navigator); console.log(`has serviceWorker: ${'serviceWorker' in navigator}`); navigator.serviceWorker.register('sw.js', { scope: './' }).then(function(registration) { var serviceWorker; if (registration.installing) { console.log('installing'); serviceWorker = registration.installing; document.querySelector('#kind').textContent = 'installing'; } else if (registration.waiting) { console.log('waiting'); serviceWorker = registration.waiting; document.querySelector('#kind').textContent = 'waiting'; } else if (registration.active) { console.log('active'); serviceWorker = registration.active; document.querySelector('#kind').textContent = 'active'; } if (serviceWorker) { console.log(`state: ${serviceWorker.state}`); serviceWorker.addEventListener('statechange', function (e) { console.log(`state: ${e.target.state}`); }); } }).catch (function (error) { console.log(error); }); }
sw.js
const VERSION = 'v1'; self.addEventListener('install', function(e) { console.log(`install event !!`, e); // キャッシュ完了まで待機する e.waitUntil( caches.open(VERSION).then((cache) => { return cache.addAll([ './index.html', './main.js', './ServiceWorkerTest.js', ]); }) ); }); self.addEventListener('fetch', function(e) { console.log('fetch event !!', e); e.respondWith( // リクエストに一致するデータがキャッシュにあるかどうか caches.match(e.request).then(function(cacheResponse) { console.log('has cache: ', cacheResponse); // キャッシュがあればそれを返す、なければリクエストを投げる return cacheResponse || fetch(e.request).then(function(response) { return caches.open(VERSION).then(function(cache) { // レスポンスをクローンしてキャッシュに入れる cache.put(e.request, response.clone()); // オリジナルのレスポンスはそのまま返す return response; }); }); }) ); }); self.addEventListener('activate', function(e) { console.log(`activate event !!`, e); });
テスト方法
以下のいずれかで動作確認できる。
- ローカルサーバ
- HTTPSサーバ
ローカルサーバ
- ターミナルを起動する
- コードをダウンロードする
git clone https://github.com/ytyaru/JS.ServiceWorker.20210113144638
cd JS.ServiceWorker.20210113144638/docs/3
でカレントディレクトリを移動する- ローカルサーバを起動する
python3 -m http.server 8000
- コードをダウンロードする
- ブラウザを起動する
以上で、sw.js内の3つのイベントが実行されたことを確認できる。
参考
HTTPSサーバ
ServiceWorkerはHTTPSサーバでないと動作しないらしい。GitHub PagesならOK。テストというか本番。
所感
使いこなすの大変そう
使いこなすの大変そう。キャッシュの処理をゴリゴリ書かねばならないのが大変。今回のサンプルは古くなったファイルを削除する処理がない。本番時はそういうのも書かないと。
ローカルファイルでは動かない
HTMLファイルをローカルで開いても動かない。file://
環境では動かない。
私としては、HTMLファイルを保存しておいて動作して欲しかった。
ローカルサーバなんて起動したくない! と思うでしょ? したらCORSエラーになる。
ローカルファイルだとCORSエラーになる
JSのimportが使えなくなる
JSのimport
,export
, HTMLの<script type="mode">
が使えなくなる。
ローカルサーバを起動せず、file://
で開く。するとCORSエラーになる。import
,export
を使いたくばCORSエラーを回避するしかない。そのための方法が、ローカルサーバである。今回はServiceWorkerもローカルサーバが必要。ならもうローカルサーバ使うしかないじゃんね。
べつにimport
,export
だけなら使わなくても書ける。ちょっと面倒だけど、htmlに<script src="A.js">
,<script src="B.js">
みたいに書けばいい。だが、先述のとおり、今回はServiceWorkerでもローカルサーバが必要。ならもうローカルサーバ使ってimport
もしたほうがいい。
こういう判断がむずかしい。アレとコレの仕様を把握しつつ、何ができるのか、どうやるのが目的に合っているか、考えなきゃいけない。JavaScriptは環境差によるコード方法、テスト方法が激ムズだから嫌い。
ローカルサーバで表示しないと動かない
以下のようにローカルサーバを起動し、ブラウザでlocalhost:8000
としないと閲覧できない。
python3 -m http.server 8000
http://0.0.0.0:8000/
ではServiceWorkerオブジェクトが見つからない
ローカルサーバを起動したまではいい。けどURLはlocalhost:8000
でないとServiceWorkerオブジェクトが見つからない。仕様。そのため、ServiceWorkerの動作確認ができない。
紛らわしいことに、python3 -m http.server 8000
でローカルサーバを起動させたら、以下のようなURLを参照しろと出る。たしかにそれでHTMLは表示される。だが、ServiceWokerオブジェクトは見つからない。
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
対象環境
- Raspbierry pi 4 Model B
- Raspberry Pi OS buster 10.0 2020-08-20 ※
- bash 5.0.3(1)-release
$ uname -a Linux raspberrypi 5.4.83-v7l+ #1379 SMP Mon Dec 14 13:11:54 GMT 2020 armv7l GNU/Linux