michimani.net

Vue.js で作った Web アプリを PWA 化してみました

2019-05-23

先日作った Web アプリ ココイチ注文料金簡易カリキュレータ を PWA 化した話です。

PWA・・・PWAとは、「Progressive Web Apps」の略称で、モバイル向けWebサイトをGooglePlayストアなどで見かけるスマートフォン向けアプリのように使える仕組みです。 ([PWAとは(Progressive Web Appsとは) | SEO用語集:意味/解説/SEO効果など [SEO HACKS]](https://www.seohacks.net/basic/terms/pwa/))

作った経緯や使っているサービスなどについては一つ前の記事を見てください。

概要

最初から PWA 対応したアプリケーションを作る場合は、 Vue CLI でプロジェクトを作る際に Progressive Web App (PWA) Support にチェックを入れておきます。
そうすると、プロジェクトの作成と同時に必要なファイル (manifest.json, 各サイズのアイコンなど) が作成されます。

今回は既存の Vue アプリケーションを PWA 化するので、 Progressive Web App (PWA) Support にチェックを入れたときと入れないときとの差分を補う形で対応しました。

ちなみに PWA 化することで出来るようになること (Push 通知やオフラインキャッシュなど) はいろいろあるのですが、今回の目的はとりあえず ホーム画面に追加してネイティブアプリっぽく使えるようにする ことです。

手順

1. 必要なパッケージのインストール

package.json に追記して、 PWA 化に必要なパッケージをインストールします。

@@ -9,6 +9,7 @@
  },
  "dependencies": {
    "core-js": "^2.6.5",
+   "register-service-worker": "^1.6.2",
    "vue": "^2.6.10",
    "vue-class-component": "^7.0.2",
    "vue-property-decorator": "^8.1.0"
@@ -16,6 +17,7 @@
  "devDependencies": {
    "@vue/cli-plugin-babel": "^3.7.0",
    "@vue/cli-plugin-eslint": "^3.7.0",
+   "@vue/cli-plugin-pwa": "^3.7.0",
    "@vue/cli-plugin-typescript": "^3.7.0",
    "@vue/cli-service": "^3.7.0",
    "@vue/eslint-config-prettier": "^4.0.1",
$ npm install

2. Service Worker を登録する処理の追加

Service Worker を使うことによって Push 通知やオフラインキャッシュなど、よりネイティブアプリに近い動作を実現することが出来ます。 (もしかしたら ホーム画面への追加だけなら必要ない?ちょっとこの辺は)

src の直下に下記のような registerServiceWorker.ts を作成します。

/* eslint-disable no-console */

import { register } from "register-service-worker";

if (process.env.NODE_ENV === "production") {
  register(`${process.env.BASE_URL}service-worker.js`, {
    ready() {
      console.log(
        "App is being served from cache by a service worker.\n" +
          "For more details, visit https://goo.gl/AFskqB"
      );
    },
    registered() {
      console.log("Service worker has been registered.");
    },
    cached() {
      console.log("Content has been cached for offline use.");
    },
    updatefound() {
      console.log("New content is downloading.");
    },
    updated() {
      console.log("New content is available; please refresh.");
    },
    offline() {
      console.log(
        "No internet connection found. App is running in offline mode."
      );
    },
    error(error) {
      console.error("Error during service worker registration:", error);
    }
  });
}

そして src/main.ts 内で import します。

@@ -1,5 +1,6 @@
  import Vue from "vue";
  import App from "./App.vue";
+ import "./registerServiceWorker";

  Vue.config.productionTip = false;

3. アイコンの作成

PWA 化するとホーム画面やスプラッシュ画像に表示されるアイコン画像が必要になるので、 public/img/icons 配下に下記のアイコン画像を準備します。

public/img/icons
├── android-chrome-192x192.png
├── android-chrome-512x512.png
├── apple-touch-icon-120x120.png
├── apple-touch-icon-152x152.png
├── apple-touch-icon-180x180.png
├── apple-touch-icon-60x60.png
├── apple-touch-icon-76x76.png
├── apple-touch-icon.png
├── favicon-16x16.png
├── favicon-32x32.png
├── msapplication-icon-144x144.png
├── mstile-150x150.png
└── safari-pinned-tab.svg

apple-touch-icon.png180px x 180pxsafari-pinned-tab.svg16pt x 16pt 、 その他のアイコンはファイル名の通りのサイズ (px) で作成します。

4. manifest.json の作成

今回の目的であるホーム画面へのアイコン追加を実現するためのファイルです。 public 直下に作成します。

{
  "name": "ココイチ注文料金簡易カリキュレータ",
  "short_name": "coco1cal",
  "icons": [
    {
      "src": "./img/icons/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "./img/icons/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    },
    {
      "src": "./img/icons/apple-touch-icon-60x60.png",
      "sizes": "60x60",
      "type": "image/png"
    },
    {
      "src": "./img/icons/apple-touch-icon-76x76.png",
      "sizes": "76x76",
      "type": "image/png"
    },
    {
      "src": "./img/icons/apple-touch-icon-120x120.png",
      "sizes": "120x120",
      "type": "image/png"
    },
    {
      "src": "./img/icons/apple-touch-icon-152x152.png",
      "sizes": "152x152",
      "type": "image/png"
    },
    {
      "src": "./img/icons/apple-touch-icon-180x180.png",
      "sizes": "180x180",
      "type": "image/png"
    }
  ],
  "start_url": "./index.html",
  "display": "standalone",
  "background_color": "#FFFFFF",
  "theme_color": "#4e342e"
}

icons で最低限必要なのは android-chrome-192x192.pngandroid-chrome-512x512.png です。ただ、 iOS でもホーム画面への追加を実現したい場合は、その他のファイルも指定する必要があります。

また、 iOS のホーム画面アイコンは透過画像が使用できません。というか、使用はできるのですが、透過部分が黒で塗りつぶされます。
なので、 Android 用アイコンには透過画像を使いたい場合、 iOS 用のアイコンには透過部分を含まない画像を用意したほうがよいです。

background_color はスプラッシュ画像の背景色になります。

theme_color はツールバーの色になります。

その他、 manifest.json に関する内容は The Web App Manifest | Web Fundamentals | Google Developers をご覧ください。

5. index.html への追記

4 までの手順で PWA 化は完了しているのですが、追加で対応が必要な内容がありました。

manifest.json の theme_color が反映されない

manifest.jsontheme_color はツールバーの色になると書きましたが、ここを変更してもなぜか Vue.js デフォルトの #4DBA87 になってしまう問題がありました。 (Android 端末: Pixel 3a)

PWA化したのはいいけど、テーマカラーが変わらないのはなぜ。manifest.json の値は変わってるのに。 pic.twitter.com/6kNVRyRrf3

— よっしーCBR789RR (@michimani210) May 22, 2019

調べてみると、どうやら Vue.js で作った PWA では manifest.jsontheme_color が反映されないため、 head 内に直接メタタグで指定する必要があるらしいです。

ということで head 内に追記したところ、無事に反映されました。

@@ -14,6 +14,7 @@
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
+ <meta name="theme-color" content="#4e342e">
  <link rel="icon" href="<%= BASE_URL %>favicon.ico">

iOS では manifest.json を読んでくれない

PWA の仕様や manifest.json については Google が進めている内容で、 iOS Safari ではまだ未対応な部分も多々あるようです。
そのため、 manifest.json の内容を head 内にメタタグで追記する必要があります。ただ、すべてを書くとなると面倒なので、 Google が用意ししている PWACompact を利用します。

使い方は簡単で、 GitHub の readme にある通り head 内に下記の内容を追記するだけです。ただし、 readme と違う点は、 manifest.webmanifest ではなく manifest.json としているところです。

@@ -5,6 +5,10 @@
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width,initial-scale=1.0">
  <meta name="theme-color" content="#4e342e">
+ <link rel="manifest" href="manifest.json" />
+ <script async src="https://cdn.jsdelivr.net/npm/pwacompat@2.0.8/pwacompat.min.js"
+     integrity="sha384-uONtBTCBzHKF84F6XvyC8S0gL8HTkAPeCyBNvfLfsqHh+Kd6s/kaS4BdmNQ5ktp1"
+     crossorigin="anonymous"></script>
  <link rel="icon" href="<%= BASE_URL %>favicon.ico">

これによって、 iOS 用に必要なメタタグが自動で挿入されるようになります。

Lighthouse で確認

Lighthouse でスコアを確認してみます。

Lighthouse score all

全体的に高いです。まあ、外部 API を使ったり複雑なデータの持ち方をしているわけではないので、スコアが高いのは当たり前かもしれません。

PWA の項目について見てみます。

Lighthouse score PWA

全部オッケーっぽいですね。

みんなでココイチへ行こう!

Vue.js で作ったアプリケーションを PWA 化してみた話でした。
iOS/Android のネイティブアプリを作るとなると、デベロッパー登録とかストアへの申請とかハードルがいくつかありますが、 PWA の技術を使えば Web アプリケーションをネイティブアプリっぽくできるので、すごいですね。 (語彙力)
現状では iOS で対応が不完全だったりするので、今後そのあたりが統一されていけばスマホアプリ事情が変わっていくかもしれませんね。

ということで、PWA 化した ココイチ注文料金簡易カリキュレータ 、ぜひ使ってみてください。アクセスして「ホーム画面へ追加」するだけです。

ココイチ美味しいですよ🍛


comments powered by Disqus