アイスタイル Advent Calendar 2016 1o日目は@kubotakがお届けします。
サーバーサイドエンジニアをやってますが、今年もフロントエンドよりな記事を書きます。
ServiceWorker
HTML5Conference2015でもこれについて触れている講義が多かった(個人比)のでご存じの方も多いと思います。
通常のjavascript実行とは異なりブラウザに登録することでバックグラウンドで動作する仕組みです。
この仕組を用いることでブラウザで以下の機能を実現することができます
- キャッシュを用いたオフライン表示
- プッシュ通知
- バックグラウンド同期
- etc
とても便利そうでしょう?ただし以下の条件が必要ですので注意が必要です
- httpsもしくはlocalhost(開発用)
- 対応ブラウザが限られている(2016/12現在)
本稿ではキャッシュを用いたオフライン表示について、実践してみたいと思います。
ちなみに、弊社サービスでも自分の担当しているサイトではひっそり導入をしています(対応ブラウザが増えて、採用が増えるといいな)
serviceworker.jsを登録する
ServiceWorkerは仕組みの呼称ですので登録させるjavascriptの名前はsw.jsでもhoge.jsでも問題ありません。今回はserviceworker.jsというファイルを作成し、そのjsファイルをServiceWorkerとして登録させます。
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('serviceworker.js', {scope: './'})
.then(registration => {
// Success
console.log('Service Worker Registered');
}).catch(error => {
// Error
console.error(error);
});
}
{scope: './'}
とすることにより、後述するfetch
イベントの際に、このスコープ以下のファイルが対象になる。
serviceworker.jsでページや画像をキャッシュさせる
const VERSION = 1;
const STATIC_CACHE_NAME = `cache_${VERSION}`;
const ORIGIN = `${location.protocol}//${location.hostname}` + (location.port ? ':' + location.port : '');
// Cacheするファイル
const STATIC_FILES = [
'https://fonts.googleapis.com/css?family=Josefin+Sans',
'https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css',
`${ORIGIN}/`,
`${ORIGIN}/?utm_source=web_app_manifest`,
// `${ORIGIN}/img/image.png`, // sample
];
let STATIC_FILE_URL_HASH = {};
STATIC_FILES.forEach(x => {
STATIC_FILE_URL_HASH[x] = true;
});
// install
self.addEventListener('install', e => {
e.waitUntil(
caches.open(STATIC_CACHE_NAME).then(cache => {
return Promise.all(STATIC_FILES.map(url => {
return fetch(new Request(url)).then(response => {
if (response.ok)
return cache.put(response.url, response);
return Promise.reject(
`Invalid response. URL: ${response.url}
Status: ${response.status}`);
});
}));
}));
});
ServiceWorkerが登録される際はこちらのinstall
イベントが実行されます。
self.addEventListener('install', e => {});
このイベントの中のcache.putで、ブラウザのCacheStorageにレスポンスが格納されます。
Cacheした情報を返す
Cacheしただけではオフライン時に表示はされません。
既にCache済みのデータがある場合はCacheをブラウザに返却する必要があります。
self.addEventListener('fetch', e => {
if (!STATIC_FILE_URL_HASH[e.request.url]) return;
e.respondWith(caches.match(e.request, {cacheName: STATIC_CACHE_NAME}));
});
Cacheを削除する
activate
イベントのタイミングでcacheした内容とSTATIC_CACHE_NAME
が異なっている場合に対象のcacheを削除します。
// Delete Cache
self.addEventListener('activate', e => {
e.waitUntil(
caches.keys().then(keys => {
let promises = [];
keys.forEach(cacheName => {
if (cacheName != STATIC_CACHE_NAME)
promises.push(caches.delete(cacheName));
});
return Promise.all(promises);
}));
});
ServiceWorkerからレスポンスを返却する
fetch
イベントハンドラでe.respondWith()
としている箇所では、サーバーからの返却ではなくServiceWorkerからブラウザへデータを返却しています。
ここではcacheしたファイルをブラウザに返却しているため、オフラインでの動作が可能となります。
self.addEventListener('fetch', e => {
if (!STATIC_FILE_URL_HASH[e.request.url]) return;
e.respondWith(caches.match(e.request, {cacheName: STATIC_CACHE_NAME}));
});
※ ES2015で記述しているため公開時にはトランスパイルをしてES5にする
DEMO
DEMOサイトを用意しました。
github.io(GitHubPages)を利用してます。httpsなのでServiceWorkerを手軽に動作させることができます。
ぜひAndroid4.4以降の最新のChromeやFireFoxでご確認ください(safari非対応なので)
Chromeでアクセス後はそのページをホーム画面に追加してみてください。
ネイティブアプリのようにホームにアイコンが追加され、タップすることでブラウザ枠を表示しないで立ち上がると思います。
本稿では触れてませんがこれがProgressiveWebAppsと呼ばれるものになります。興味が湧いた方はぜひそちらについても調べていただければと思います。
さて、ページが表示されたでしょうか。続いては機内モード等で通信を切断させてください。
その状態で同じようにまたDEMOサイトを立ち上げてみてください。
・・・表示、できているでしょうか?
これがServiceWorkerのいち機能であるオフライン表示です。
Next Istyle Advent Calendar 2016
fagai presents 「Laravelのソースを追えるようになるにはどうしたらよいのか?」
ご期待下さい!