1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【備忘録】Vue.js(SPA) + PWA + ServiceWorker 環境構築メモ

Last updated at Posted at 2023-06-29

すごい分かり難いのでメモしとく。

用語 etc.

環境

  • Vue.js - v3.x
  • @vue/cli-plugin-pwa - v5.x
  • register-service-worker - v1.7.2

1. @vue/cli-plugin-pwa を導入

公式:@vue/cli-plugin-pwa

vue createする際に同時に導入するか、既存のプロジェクトに後で追加(公式)するか

1.1 (参考)関連ファイル

  • ./src/main.js
    • ./src/registerServiceWorker.jsimportする
  • ./src/registerServiceWorker.js
    • ServiceWorker の登録等。./dist/service-worker.jsregisterする
  • ./src/sw.js
    • workbox 経由でビルドされるServiceWorkerの元。カスタマイズコードなどもこのファイルに追加する。vue.configpwa.workboxOptions.swSrcで指定。
  • ./vue.config.js
    • workboxの設定等。
  • ./dist/service-worker.js
    • 実際にクライアント側でハンドルされるworkboxでビルドされた ServiceWorkerの実態。vue.configpwa.workboxOptions.swDestで指定。

1.2 (参考) 注意点

  • Service Worker は、productionモードでしかビルドされない。npm run serve等では実行不可。
  • localhostでもproductionモードでビルド・実行ならService Workerは実行可能。
  • Workbox(公式)をある程度理解する必要がある。
    • 簡単な話、Service Workerの実態をビルドしてくれるプラグイン
  • 特に何も指定しない場合、./dist配下は、全てprecacheされる模様
    • http(s)プロトコルでprecacheされる。特別なプロトコルで云々...ではない。
    • 全てのprecache時にエラーなしでinstallは正常と判断。一つでもエラーがあれば失敗。ServiceWorker.state === "redundant"になる。(ServiceWorker.state | MDN
    • ので、./dist/404.html等があると404エラーとなりinstallは必ず失敗する。何かしらの対処が必須。

2. vue.configの設定

重要なのは以下。
webpackではなくpwa配下のworkboxのが重要

vue.config.js
module.exports = defineConfig({
  //...
  pwa: {
    // configure the workbox plugin
    workboxPluginMode: "InjectManifest", // カスタマイズされたServiceWorkerを運用したいならこっち。
    workboxOptions: {
      // swSrc is required in InjectManifest mode.
      swSrc: "./src/sw.js", // workbox でビルドされるServiceWorkerの元
      swDest: "./service-worker.js", // workbox でビルドされたServiceWorkerの実態。./distにビルドされる
      // ...other Workbox options...
      exclude: [/404\.html/], // precache から除外するファイル
    },
  },
  configureWebpack: {
    // webpack の設定
    // pwa + ServiceWorker では、特別変更する必要はない
  },
});

3. ./src/sw.jsWorkboxによるprecacheを実装する

Workbox の injectManifest モードでprecacheを利用する際は必須。
指定しない場合は build でエラーになる。

Precaching with Workbox#Side-by-side comparison | Chrome Developers

./src/sw.js
import { precacheAndRoute } from "workbox-precaching";

precacheAndRoute(self.__WB_MANIFEST);

//以降は実際に動作させたい Service Worker の処理

self.addEventListener("fetch", (event)=>{
  console.log("[sw.js] fetch", event);
});

4. Service Worker の動作確認

ビルド→LocalWebServer起動→index.htmlにアクセスで可能

4.1 ビルド

bash
$ npm run build

( 省略 )

 DONE  Build complete. The dist directory is ready to be deployed.
 INFO  Check out deployment instructions at https://cli.vuejs.org/guide/deployment.html

4.2 ./dist/service-worker.jsがあるか確認

まぁ、ある

4.3 Local Web Server の起動

firebase emulators の場合

bash
$ firebase emulators:start

( 省略 )

┌─────────────────────────────────────────────────────────────┐   
│ ✔  All emulators ready! It is now safe to connect your app. │   
│ i  View Emulator UI at http://127.0.0.1:4000/               │   
└─────────────────────────────────────────────────────────────┘   

4.4 localhost にブラウザでアクセス

firebase emulators Hosting | http://localhost:5000

以下の様なログがブラウザのコンソールに出力されていれば大体成功

console.log
Service worker has been registered.
New content is downloading.
Content has been cached for offline use.
App is being served from cache by a service worker.
For more details, visit https://goo.gl/AFskqB

4.4.1 初回アクセス時のイベント発行順序

初回は以下の順で./src/registerServiceWorker.js内のイベント(関数)が発行される。
(※正常ケース)

  1. registered()
  2. updatefound()
  3. cached()
  4. ready()

4.4.2 2回目以降

(※正常ケース)

  1. ready()
  2. registered()

4.4.3 異常ケース

初回アクセス時。ready()の発行がないパターン。
(めちゃくちゃ分かりづらい

console.log
Service worker has been registered.
New content is downloading.
  1. registered()
  2. updatefound()

この場合は、F5でブラウザをリロードしてみる

console.log
Service worker has been registered.
registerServiceWorker.js:21 New content is downloading.
Uncaught (in promise) bad-precaching-response: bad-precaching-response :: [{"url":"http://127.0.0.1:5000/404.html","status":404}]
    at A._handleInstall (http://127.0.0.1:5000/service-worker.js:2917:217483)
    at async A._handle (http://127.0.0.1:5000/service-worker.js:2917:216888)
    at async A._getResponse (http://127.0.0.1:5000/service-worker.js:2917:215954)

分かり辛いがworkboxprecache時に404エラーが発生してregisterが失敗しているらしい。
ので、vue.configpwa.workboxOptions.excludeに、precacheしないファイルを指定する必要がある。(本投稿内で説明済み)

ext.1. Reload からの 404 に対応する

PWA というか、SPA で必要

ext.1.1 Web Hash History Mode 利用の場合

Web Server 側の設定変更ができない場合等
(※Github Pages で Vue.js アプリをホストする場合等

ext.1.1.1 vue-router を Web Hash History Mode にする

vue-router https://router.vuejs.org/api/#Functions-createWebHashHistory

./src/router/index.js
- import { createRouter, createWebHistory } from "vue-router";
+ import { createRouter, createWebHashHistory } from "vue-router";
import HomeView from "../views/HomeView.vue";

// (省略)

const router = createRouter({
-  history: createWebHistory(),
+  history: createWebHashHistory(),
  routes,
});

export default router;

ext.1.1.2 BASE_URL を変更する

./src/router/index.js
// (省略)

+ const BASE_URL = (() => {
+   if (process.env.NODE_ENV === "production") {
+     return process.env.BASE_URL + "<your-github-project-name>";
+   } else {
+     return process.env.BASE_URL;
+   }
+ })();

const router = createRouter({
  history: createWebHashHistory(),
+   history: createWebHashHistory(BASE_URL),
  routes,
});

export default router;

ext.1.2 Web History Mode 利用の場合

Web Server 側の設定を変更できる場合等に使える

ext.1.2.1 vue-router を Web History Mode にする

vue-router https://router.vuejs.org/api/#Functions-createWebHistory

./router/index.js
- import { createRouter, createWebHashHistory } from "vue-router";
+ import { createRouter, createWebHistory } from "vue-router";
import HomeView from "../views/HomeView.vue";

// (省略)

const router = createRouter({
-  history: createWebHashHistory(),
+  history: createWebHistory(),
  routes,
});

export default router;

ext.1.2.2 Web Server に rewrite を設定する

※SPAでは、index.htmlのみしか実態を持たないが、内部で利用されるURL自体は./hoge./fuga等は十分ありえる。ブラウザが./hogeの時点でreload(もしくは別APIからのredirect)すると http request がWebサーバーに発行されてしまい、404 Not Foundに必ずなる。その動作への対処。
※本投稿では、Firebase Hosting に限定。

firebase.json
{

  "hosting": {
    "public": "dist",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
+    "rewrites": [
+      {
+        "source": "**",
+        "destination": "/index.html"
+      }
    ]
  }

ext.1.3 Webpack Dev Server の場合

ローカルでの開発時に有用。

ext.1.3.1 webpack config に rewrites を設定する

webpack failback https://webpack.js.org/configuration/dev-server/#devserverhistoryapifallback

vue.config.js
const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({

// (省略)

  configureWebpack: {
    devServer: {
      historyApiFallback: {
        rewrites: [{ from: /\*/, to: "/index.html" }],
      },
    },
  },

// (省略)

});

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?