Service Workerのキャッシュ制御
PWAでServiceWorkerを扱ったのですが、ふわっとしか触れられておらず、分かりづらかったので、ひとまずキャッシュの制御に関して実用レベルでやってみた実例の紹介です。
Service Workerのおさらい
通常は、Webサイトを閲覧するときに、
閲覧者 = Browser <-> Web Server
という形で通信をしますが、Service Workerを使うと、
閲覧者 = Browser <-> **Service Worker** <-> Web Server
となり、すべての通信は、Service Workerを介して行われることになります。
キャッシュを完全に制御できる!?
いろいろ Service Worker の機能がありますが、今まで、ブラウザ任せで「キャッシュされているのかどうか分からない」状態だったものが、明示的に
- これはキャッシュする
- これはキャッシュしない
- 今はキャッシュを読み込まない
などの制御ができるようになります。
今回の仕様
- 初回のインストール時は特に何もしない
- リクエストがきたら、基本的にキャッシュ追加していく
- ただし、特定のドメインのファイルは明示的にキャッシュしない
- 特定のファイルを明示的にキャッシュする
- 全キャッシュを明示的に削除する
sw.js
var CACHE_NAME = "cache-name-here";
//キャッシュしないドメイン名のリスト
var noCacheList = [
"www.google-analytics.com",
"localhost"
];
//インストール時の処理
self.addEventListener('install', function(e) {
//何もしない
return;
});
//フェッチ時(何かしらの通信リクエスト)があったときの処理
self.addEventListener('fetch', function (e) {
e.respondWith(
caches.open(CACHE_NAME).then(function (cache) {
return cache.match(e.request).then(function (response) {
if (response) {
// e.requestに対するキャッシュが見つかったのでそれを返却
return response;
} else {
// キャッシュが見つからなかったので取得
return fetch(e.request.clone()).then(function (response) {
//キャッシュしないドメイン名のリストに含まれていないかを確認する
var url = e.request.url;
var flg = true;
for(var cnt=0;cnt<noCacheList.length;cnt++){
if(url.indexOf(noCacheList[cnt])!=-1){
flg = false;
}
}
if(flg){
//キャッシュに追加する
cache.put(e.request, response.clone());
}else{
//リストにあったのでキャッシュ追加はしない。
}
// 取得したリソースを返却
return response;
});
}
});
})
);
});
//メッセージ受信時の処理
self.addEventListener('message', function(e) {
switch (e.data['command']) {
case 'clearCacheAll':
//全キャッシュをクリアする
e.waitUntil(
caches.keys().then(function(names) {
for (let name of names){
caches.delete(name);
}
})
);
break;
case 'getCache':
//URLリストのキャッシュを取得する
e.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
this.console.log(e.data['files']);
// 指定されたリソースをキャッシュに追加する
return cache.addAll(e.data['files']);
})
);
break;
}
});
呼び出し側のJavaScript
//サービスワーカー登録
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js')
.then(
function (registration) {
if (typeof registration.update == 'function') {
registration.update();
}
})
.catch(function (error) {
_c_("Error Log: " + error);
});
}
//メッセージ送信例:キャッシュ取得
var swctrl = navigator.serviceWorker.controller;
swctrl.postMessage({
'command':'getCache',
'files':['https://wwww.sample.data/abc.png','https://wwww.sample.data/def.png']
});
//メッセージ送信例:全キャッシュ削除
var swctrl = navigator.serviceWorker.controller;
swctrl.postMessage({
'command':'clearCacheAll'
});