キャッシュ制御
概要
Service Worker の目的の一つはキャッシュ制御によるサイトの高速化である。ブラウザのキャッシュをサイト実装者が詳細に制御することで、動的に変化しないコンテンツ (HTML, CSS, JS, 画像等) をデバイスローカルにキャッシュし Web アプリケーションの表示を高速化する。特に、動作に必要なファイル全てがキャッシュ可能であれば Web アプリはオフラインでも起動することができる。
このキャッシュ制御はブラウザがコンテンツを取得する部分を Service Worker の fetch イベントハンドラに代行させることで行われる。
Service Worker はブラウザからの fetch イベント ─ ネットワークからのコンテンツ取得要求 ─ が発生したときに、必要に応じてキャッシュに保存されている内容でレスポンスを置き換えることができる。
Service Worker が介入できるリクエストはスコープ内のコンテンツ自体及びそのコンテツから発生するリクエストのみである。
一般的なユースケースは Service Worker のインストール時に基本的な HTML や CSS, JavaScript ファイルをデバイスにキャッシュし、次回以降はネットワークにアクセスすることなくキャッシュから参照する。
プログラミング
事前キャッシュを有効にする
以降、コードはイベントハンドラごとに記述する。キャッシュの操作は CacheStorage
参照。
install イベント
Service Worker は install イベント発生時に URL を指定してキャッシュへ追加することで事前キャッシュを行うことができる。
var APP_VERSION = "hazm.at:content:0.0.1b";
self.addEventListener("install", function(e) {
e.waitUntil(
caches.open(APP_VERSION).then(function(cache) {
return cache.addAll([
"/myapp/index.html", "/myapp/style.css",
"/myapp/app.js", "/myapp/logo.png"
]);
})
);
});
active イベント
キャッシュの名前空間にバージョンを付けることで Web アプリが更新された時に古いバージョンで使用されていたキャッシュを適切に削除することができる (例では Maven 風にしているが Web アプリで一意性が保証できればなんでも良い)。この処理は activate イベントで行う。
self.addEventListener("activate", function(e) {
e.waitUntil(
caches.keys().then(function(keys) {
return Promise.all(keys.map(function(key) {
if (key !== APP_VERSION) {
return caches.delete(key);
}
});
})
);
});
activate イベントは Service Worker の URL が最初に登録される時のみ発動し、以降の更新では install イベントのみという動作をする。従って Google や MDN のチュートリアル通り、本当に activate イベントでキャッシュのクリアをするのが正しいか確証がない。
fetch イベント
fetch イベント時にキャッシュを参照し、事前にキャッシュされた静的コンテンツが存在すればキャッシュから返す。そうでない動的コンテンツであればリモートサーバから取得する。
self.addEventListener("fetch", function(e) {
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
});
この処理を拡張し、リモートサーバから取得したレスポンスを新しくキャッシュする、オフラインなどの理由で取得に失敗した場合に fallback レスポンスを返す、といった実装が可能である。このような実装の詳細は MDN を参照。
fetch()
関数はいくつかのバリエーションがある。
fetch(e.request.url);
fetch(e.request);
fetch(url, {
credentials: "include"
})
fetch()
で行うリクエストに Cookie などのクレデンシャル情報を送信するにはオプションに credential:"include"
を追加する。
レスポンスを置き換える
fetch イベントではレスポンスを静的なデータに置き換えることができる。
Response
インスタンスでレスポンスを置き換えることができる。
self.addEventListener("fetch", function(e) {
e.respondWith(new Response("<p>Hello from your friendly neighbourhood service worker!</p>", {
headers: { "Content-Type": "text/html" }
}));
});
参考文献
- Using Service Workers (MDN Web Docs)