トップへ(mam-mam.net/)

Webページがアプリのようにインストールできてオフラインでも動作するPWAを作る

Webページがアプリのようにインストールできてオフラインでも動作するPWAを作る

PWA(Progressive Web Apps)とはWebページがアプリのようにインストールできてオフラインでも動作する機能です。
Androidはかなり対応している機能が多いですが、iPhone(iOS)は対応している機能が少ないです。

これから説明するファイルは全て
/javascript/pwa/
ディレクトリに配置するものとして説明します。

/javascript/pwa/index.htmlメインファイル
/javascript/pwa/manifest.jsonマニフェスト
/javascript/pwa/service-worker.jsサービスワーカー
/javascript/pwa/icon.pngアイコン(192x192)

index.htmlファイル

navigator.serviceWorker.register('./service-worker.js',
でサービスワーカー(バックグラウンド処理を担当する特別なJavaScript)のスクリプトファイルを指定しています。

{scope: '/javascript/pwa/'}
でスコープを指定しています。 つまり、どのパス以下の通信やデータのやり取りを途中で横取りするできるかを決定します。

<html>
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1">
  <title>何らかのタイトル</title>
  <meta name="Description" content="説明">
  <META name="Keywords" content="キーワード">

  <!-- マニフェストファイルを指定する -->
  <link rel="manifest" href="manifest.json">

  <!-- アドレスバー等のブラウザのUIを非表示 -->
  <meta name="mobile-web-app-capable" content="yes">

  <!-- default(Safariと同じ) black(黒) black-translucent(ステータスバーをコンテンツに含める) -->
  <meta name="apple-mobile-web-app-status-bar-style" content="black">

  <!-- ホーム画面に表示されるアプリ名 -->
  <meta name="apple-mobile-web-app-title" content="アプリ名">

  <!-- ホーム画面に表示されるアプリアイコン -->
  <link rel="apple-touch-icon" href="icon.png">

  <meta name="apple-touch-fullscreen" content="yes">
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">

  <script>
    //サービスワーカーの登録
    if ('serviceWorker' in navigator) {
      navigator.serviceWorker.register(
        './service-worker.js',
        //スコープを省略すると service-worker.js のディレクトリ以下となる
        {scope: '/javascript/pwa/'} 
      ).then(function(reg){
        console.log('ServiceWorkerの登録成功 スコープ: ', reg.scope);
      }).catch(function(err){
        console.log('ServiceWorkerの登録失敗: ', err);
      });
    }
  </script>

</head>
<body>
  PWAアプリのコンテンツ
</body>
</html>

manifest.json ファイルについて

{
  "lang":"ja",
  "name"            : "アプリケーションの名前",
  "short_name"      : "短縮アプリ名",
  "display"         : "standalone",
  "start_url"       : "https://mam-mam.net/javascript/pwa/index.html",
  "background_color": "#FFFFFF",
  "theme_color"     : "#FFFFFF",
  "icons": [
    {
      "src"  : "icon.png",
      "sizes": "192x192",
      "type" : "image/png"
    },
  ]
}

service-worker.js ファイル

CACHE_NAME は キャッシュの名前を入れます。アプリケーションがバージョンアップ時(index.html等が更新された場合)には
var CACHE_NAME='v1.1-pwa-cache-name';
のように名前を変えなければキャッシュが永遠に更新されません。

urlsToCache にキャッシュするファイルを service-worker.js を基準とする相対パスで指定します。
また、service-worker.js より上位ディレクトリを指定してもキャッシュできません。

//キャッシュの名前
var CACHE_NAME='v1.0-pwa-cache-name';
//キャッシュ対象URL一覧配列
var urlsToCache = [
  '/javascript/pwa/',
  '/javascript/pwa/index.html',
  '/javascript/pwa/icon.png',
];

//インストール処理(キャッシュに入れる)
self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(CACHE_NAME).then(function(cache) {
        return cache.addAll(urlsToCache.map(url => new Request(url, {credentials: 'same-origin'})));
    })
  );
});

//リソースフェッチ時のキャッシュロード処理
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(response){
      return response || fetch(event.request);
    })
  );
});

// アクティベーションイベント:不要なキャッシュを削除
self.addEventListener('activate', event => {
  console.log("activated");
  const cacheWhitelist = [CACHE_NAME];
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (!cacheWhitelist.includes(cacheName)) {
              //console.log('Deleting old cache:', cacheName);
              return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

実際のサンプル

インストール要件は以下のようです。

サンプルへ(別ウィンドウが開く)