PWA / Service Worker を使ってオフラインでもサイトを表示できるようにする

サービスワーカーを使ってコンテンツをキャッシュすると、ユーザーがオフラインになったときでも動作するウェブサイトやウェブアプリを作ることができます。

基本的な使い方をメモ。

Service Worker を登録

index.html
1
2
3
4
5
6
7
<html>
<body>
  <h1>Hi, this is my App!</h1>
  <script src="app.js"></script>
</body>
</html>
app.js
1
2
3
4
5
6
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js')
    .then(function() {
      console.log('Service worker registered!');
    });
}
sw.js
1
// ここに Service Worker の処理を書いていきます

Service Worker をインストールして静的ファイルをキャッシュする

sw.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// キャッシュにバージョンを付けておくと、古いキャッシュを消す時に便利
var CACHE_STATIC_VERSION = 'static-v1';

// サービスワーカーのインストール
self.addEventListener('install', function(event) {
  console.log('[Service Worker] Installing Service Worker...');

  // キャッシュできるまで次の処理を待つ
  event.waitUntil(
    caches.open(CACHE_STATIC_VERSION)
      .then(function(cache) {
        console.log('[Service Worker] Precaching App...');
        // 何でもキャッシュできる。cssとかの中で更にリクエストが発生する場合は、動的にキャッシュする必要がある(後述)
        cache.addAll([
          '/',
          '/src/css/main.css',
          '/src/js/main.js',
          '/src/images/logo.jpg',
        ]);
      })
  );
});

キャッシュを利用してコンテンツを表示する

これでオフラインでもウェブサイトが表示できるようになります。
fetch イベントを監視して、キャッシュがあればhttpリクエストしないようにしています。

sw.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var CACHE_DYNAMIC_VERSION = 'dynamic-v1';

self.addEventListener('fetch', function(event) {
  console.log('[Service Worker] Fetching something ...');
  event.respondWith(
    // キャッシュの存在チェック
    caches.match(event.request)
      .then(function(response) {
        if (response) {
          return response;
        } else {
          // キャッシュがなければリクエストを投げて、レスポンスをキャッシュに入れる
          return fetch(event.request)
            .then(function(res) {
              return caches.open(CACHE_DYNAMIC_VERSION)
                .then(function(cache) {
                  // 最後に res を返せるように、ここでは clone() する必要がある
                  cache.put(event.request.url, res.clone());
                  return res;
                })
            })
            .catch(function() {
              // エラーが発生しても何もしない
            });
        }
      })
  );
});

新しいバージョンのキャッシュがあったら古いキャッシュを削除

サービスワーカーが activate されたタイミングで処理しないと 先に fetch とかが走って表示がおかしくなってしまうので注意。

sw.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
self.addEventListener('activate', function(event) {
  console.log('[Service Worker] Activating Service Worker...');
  event.waitUntil(
    caches.keys()
      .then(function(keyList) {
        return Promise.all(keyList.map(function(key) {
          if (key !== CACHE_STATIC_VERSION && key !== CACHE_DYNAMIC_VERSION) {
            console.log('[Service Worker] Removing old cache...');
            return caches.delete(key);
          }
        }));
      })
  );
  return self.clients.claim();
});

電波ないのにサイトが開けるって良いですね。コンテンツを先読みしておいて表示を爆速にすることもできるし。
ネイティブアプリ感覚で使えそう。
Chromeがメインで対応してますが、今後対応するブラウザも増えてくると思います。

参考

https://jakearchibald.com/2014/offline-cookbook/
https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
https://developers.google.com/web/fundamentals/primers/service-workers/

何かありましたらコメントをどうぞ

comments powered by Disqus

人気の記事

950 Points チリ出身のギタリストが弾くドラゴンボールZがむちゃくちゃかっこいい…
774 Points Wordpress + Heroku + PostgreSQL + Amazon S3 = ¥0 / 無料でサイト運営
700 Points Rubyのチートシート 変数 / クラス / モジュール
524 Points Rubyのチートシート / アクティブサポート
451 Points 紙のデザイナーがウェブ開発できるようになるまでに必要なこと
435 Points Rails / Google Analyticsのデータを使って分析や管理画面のためのグラフをつくる
323 Points RailsとHerokuでノーティフィケーションをプッシュする / PusherとTurbolinksの兼ね合い
222 Points Rails / RSpec テスト書いたことない メンドクサイ(n´Д`)という時のチートシート
193 Points Rails / Ajaxを使って画面遷移しない一時保存機能をつける
193 Points Protractorでスクレイピングしてみた