すごい分かり難いのでメモしとく。
用語 etc.
- Vue.js(https://ja.vuejs.org/)
- SPA(シングルページアプリケーション | Single Page Application | MDN)
- PWA(プログレッシブウェブアプリケーション | Progressive Web Application | MDN)
-
サービスワーカー | Service Worker | MDN
-
new webpack.Worker()
とかの ウェブワーカー | Web Worker | MDN とは別のもの
-
- Workbox | Chrome Developers
環境
- Vue.js - v3.x
- @vue/cli-plugin-pwa - v5.x
- register-service-worker - v1.7.2
1. @vue/cli-plugin-pwa
を導入
vue create
する際に同時に導入するか、既存のプロジェクトに後で追加(公式)するか
1.1 (参考)関連ファイル
-
./src/main.js
-
./src/registerServiceWorker.js
をimport
する
-
-
./src/registerServiceWorker.js
- ServiceWorker の登録等。
./dist/service-worker.js
をregister
する
- ServiceWorker の登録等。
-
./src/sw.js
-
workbox
経由でビルドされるServiceWorkerの元。カスタマイズコードなどもこのファイルに追加する。vue.config
のpwa.workboxOptions.swSrc
で指定。
-
-
./vue.config.js
-
workbox
の設定等。
-
-
./dist/service-worker.js
- 実際にクライアント側でハンドルされる
workbox
でビルドされた ServiceWorkerの実態。vue.config
のpwa.workboxOptions.swDest
で指定。
- 実際にクライアント側でハンドルされる
1.2 (参考) 注意点
- Service Worker は、
production
モードでしかビルドされない。npm run serve
等では実行不可。 -
localhost
でもproduction
モードでビルド・実行ならService Workerは実行可能。- local web server があればテスト可能。
- Simple Web Server | https://simplewebserver.org/download.html
- Firebase Local Emulator Suite | https://firebase.google.com/docs/emulator-suite?hl=ja
- local web server があればテスト可能。
-
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
のが重要
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.js
に Workbox
によるprecache
を実装する
Workbox の injectManifest モードでprecache
を利用する際は必須。
指定しない場合は build でエラーになる。
Precaching with Workbox#Side-by-side comparison | Chrome Developers
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 ビルド
$ 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 の場合
$ 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
以下の様なログがブラウザのコンソールに出力されていれば大体成功
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
内のイベント(関数)が発行される。
(※正常ケース)
- registered()
- updatefound()
- cached()
- ready()
4.4.2 2回目以降
(※正常ケース)
- ready()
- registered()
4.4.3 異常ケース
初回アクセス時。ready()の発行がないパターン。
(めちゃくちゃ分かりづらい
Service worker has been registered.
New content is downloading.
- registered()
- updatefound()
この場合は、F5でブラウザをリロードしてみる
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)
分かり辛いがworkbox
のprecache
時に404エラーが発生してregister
が失敗しているらしい。
ので、vue.config
のpwa.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
- 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 を変更する
// (省略)
+ 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
- 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 に限定。
{
"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
const { defineConfig } = require("@vue/cli-service");
module.exports = defineConfig({
// (省略)
configureWebpack: {
devServer: {
historyApiFallback: {
rewrites: [{ from: /\*/, to: "/index.html" }],
},
},
},
// (省略)
});
了