IT二刀流にはプロモーションが含まれています。

JSのServiceWorkerでWebページをオフラインで表示する

JavaScriptのServiceWorkerを利用してキャッシュさせてWebページをオフラインで表示させます。

一度表示させたページをキャッシュすることで、オフラインでもWebページが表示できます。

ServiceWorker

ServiceWorker(サービスワーカー)とはブラウザとサーバの通信の間に入っていろいろ制御できます。

それによりオフライン時にキャッシュしておいた内容を表示することができます。

通信を改ざんできるリスクがあるので、httpsかlocalhostでしか動作しません。

ちょっと小難しくて今まで避けてきたんですが、PWAと合わせてオフラインアプリを作りたいので調べました。

今回はキャッシュ機能だけの調査で、push通知などはありません。

サンプルコード

今回実験に使用したサンプルコードです。

ファイル構成

service_worker
├ media
│ ├ img1.png
│ ├ img2.png
│ └ style.css
├ index.html
├ service_worker.js
└ sw.js

上記のフォルダ構成をapacheのhtdocsの下に配置。

http://localhost/service_worker/でアクセスできるようにしています。

画像はペイントで適当に用意しました。

index.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>service worker test</title>
  <link rel="stylesheet" type="text/css" href="media/style.css">
  <script src="./service_worker.js"></script>
</head>
<body>
  <h1>TEST</h1>
  <p>
    <img src="media/img1.png" width="300" height="200" />
  </p>
  <p>
    <img src="media/img2.png" width="300" height="200" />
  </p>
</body>
</html>

style.css

h1 {
  font-size: 1rem;
}

img {
  max-width: 100%;
  height: auto;
}

service_worker.js

const registerServiceWorker = async () => {
  if ('serviceWorker' in navigator) {
    try {
      await navigator.serviceWorker.register("/service_worker/sw.js", {
        scope: "/service_worker/",
      });
    } catch(e) {
      console.error(e);
    }
  }
}

registerServiceWorker();

sw.js

// キャッシュに登録する
const addResourcesToCache = async (resources) => {
  const cache = await caches.open("v1");
  await cache.addAll(resources);
};

self.addEventListener('install', event => {
  event.waitUntil(
    addResourcesToCache([
      "/service_worker/",
      "/service_worker/media/style.css",
      "/service_worker/media/img1.png",
    ]),
  );
});

// キャッシュがあれば返す、無ければネット
const cacheFirst = async (request) => {
  const cache = await caches.open("v1");
  const responseFromCache = await cache.match(request);
  if (responseFromCache) {
    return responseFromCache;
  }

  return fetch(request);
};

self.addEventListener('fetch', event => {
  if (event.request.method !== "GET") return;

  event.respondWith(cacheFirst(event.request));
});

キャッシュの確認

今回は下記の3ファイルをキャッシュするように設定しています。

  • /service_worker/
  • /service_worker/media/style.css
  • /service_worker/media/img1.png

まずは「http://localhost/service_worker/」にアクセスしてページを表示させます。

sw_ca_画像1

初回表示なのでサーバにリクエストを投げてダウンロードされます。

Sizeの項目に各ファイルのサイズが表示されています。

指定したファイルがキャッシュされているか確認します。

sw_ca_画像2

Chromeの開発者ツールを開いて「Application」タブに移動します。

StorageのCache storageに移動してキャッシュの内容を確認します。

キャッシュするように指定した3ファイルがキャッシュされていることがわかります。

これでブラウザにキャッシュされていることが確認できました。

オフラインにする

実験のためにオフラインにします。

sw_ca_画像3

apacheサーバを停止させてもいいですが、Chromeの開発ツールでOfflineにできます。

ApplicationのService workersを選んで、Offlineにチェックを入れます。

これでオフラインにできます。

ローカルにapacheを立てている場合は、LANケーブル抜いてもダメですので注意してください。

オフラインで確認

オフライン時にキャッシュされている内容が表示されるかを確認します。

ただ、ブラウザのリロードボタンを押すだけです。

memoryやdisk cacheを無効にするために「Disable cache」にチェックを入れるとテストが楽です。

スーパーリロードでもいいです。

sw_ca_画像4

Offlineでもキャッシュされていたファイルは表示されています。

img1.pngはキャッシュ指定したので表示されています。

img2.pngはキャッシュ指定していないので、表示されません。

Apache(webサーバ)を止めても同じ結果になります。

まとめ

Service workersを利用してブラウザにキャッシュさせれば、オフラインでもWebページが表示できます。

ReactなどのSPAはbuildしたjsをキャッシュさせれば、オフラインでも動くはずです。

PWAにすればオフラインでもネイティブアプリのようなアプリが実現できると思います。

データ保存もローカルストレージを利用すれば永続化できるはずです。

WebAssembly版のSQLiteを利用すれば、ローカルでリレーショナルDBを利用できるので

スケジュール管理やメモアプリなどが実現できると思います。

個人的な目標は、オフラインで動作可能なタスク管理アプリを作成することです。

個人のタスクなんてスマホ内にあれば十分ですから。

PWA + Service Worker + SQLiteで実現することを目指しています。

ITエンジニアの転職

いまITエンジニアの需要は急拡大しています。
ITエンジニアの経験があれば好条件で転職することも難しくありません。

転職ドラフト

☆ 支援ご協力のお願い ☆

この記事が「役に立った」と感じたら

投げ銭の「OFUSEで応援」で支援して頂けたら励みになります!

OFUSEのやり方(説明)

関連記事
記事特集