Vite + Reactのサイト(SPA)をPWA化する手順(vite-plugin-pwa)
概要
今回はVite + ReactのSPAをvite-plugin-pwaでPWA化する手順を紹介していきます。
PWA化するとホーム画面追加・オフライン動作・LighthouseのPWAスコア獲得と、個人開発でも嬉しい恩恵がまとめて手に入ります。
個人的にはブラウザでの表示と違い、画面上にURLが表示されないことがかなりうれしい!
AppStoreからインストールしたアプリみたいに動くのは開発者としてうれしいですよ(^^
実際に自分が作ったcopype.shinpinoshi.comこぴぺったり(ローカルストレージで動くコピペ管理アプリ)でもこの手順そのままでPWA化していて、スマホからホーム画面に追加するとアプリのように起動できます。
それではやっていきましょう(^^!
目次
そもそもPWA / SPA / Service Workerとは(用語整理)
「PWA化したSPA」と聞いてもパッと意味が掴みにくいので、先に登場する単語を整理しておきます。
- SPA (Single Page Application): 1枚のHTMLを最初に読み込んだあと、JavaScriptがルーティングや画面更新を担当するWebアプリ形式。React Routerで
//about/settingsを切り替えるアレ - PWA (Progressive Web App):
manifest.webmanifestとService Workerを備えていて、スマホのホーム画面に「インストール」できたり、オフラインでも動くWebアプリ - Service Worker: ブラウザのバックグラウンドで動くJSワーカー。ページとネットワークの間に割り込んで、キャッシュやオフライン制御をやってくれる仕組み
つまり「PWA化したSPA」とは、SPAにmanifestとService Workerを足して、ホーム追加とオフライン対応を持たせた状態のこと。
要はみんながイメージするスマホアプリそのもののように動くということです!
タイトルにあるvite-plugin-pwaはこの「manifest + Service Worker」をViteのビルドに統合してくれるプラグインです。
全体像(4ステップ)
PWA化は次の4ステップで完了します。
- ① vite-plugin-pwaを導入:
npm iしてプラグインをvite.config.jsに追加 - ② manifestを整える: アプリ名・アイコン・テーマ色など、ホーム追加されたときの見た目を決める
- ③ Service Workerのキャッシュ戦略を決める: precache + runtimeCachingでオフライン対応
- ④ Lighthouseとインストール挙動で動作確認: 本番ビルドして実際にスコアと挙動を確認
めっちゃ簡単ですよね(^^/
さっそく順にやっていきましょう。
① vite-plugin-pwaを入れて最小設定で動かす
まずプラグインをインストールします。
コマンド
1 | npm i -D vite-plugin-pwa |
インストール後、vite.config.jsに設定を追加します。
vite.config.js
1 | import { defineConfig } from "vite"; |
これだけでビルド時にService Workerが生成され、index.htmlに登録スクリプトが差し込まれる状態になります。
registerTypeには2種類あります。
autoUpdate: 新しいService Workerが出たら自動で適用(次回リロード時)。シンプルで個人開発向きprompt: 「新しいバージョンがあります」というUIをユーザーに出して手動で更新させる。業務系で更新タイミングを制御したいとき向き
特にこだわりがなければautoUpdateでOKです!
ちなみにdevOptions.enabledをfalse(既定)にしている場合、npm run dev中はService Workerが走りません(- -
PWA挙動の確認はnpm run build && npm run previewで行うのが安全です。
② manifestを整える(アプリ名・アイコン・テーマ色)
manifestはホーム画面に追加されたときの「アプリの顔」としてアプリ名やアイコン、テーマ色などを決める場所です。vite-plugin-pwaならインラインでオブジェクトを書けます。
vite.config.js
1 | VitePWA({ |
各キーの意味はこんな感じ。
- name / short_name
- ホーム画面やアプリ一覧に出る名称。short_nameは12文字以内が目安
- start_url / scope
- アプリ起動時に開くURLと、Service Workerが制御する範囲
- display: standalone
- ブラウザのアドレスバーを隠してネイティブアプリ風に起動
- theme_color / background_color
- アプリ起動時のスプラッシュ画面・タイトルバー色
- icons
- any(通常用)とmaskable(角丸を被せても潰れないセーフゾーン版)の両方を用意
アイコン画像はpublic/直下に置いておけば、ビルド時にdist/ルートへコピーされます。
サイズは192×192と512×512、それにmaskable用の512×512の3枚あればOK!
maskableアイコンを用意しないと、Androidのホーム画面で白い枠が出たり、中身が切れて見えたりします。
ちょっと面倒ですが、セーフゾーン(中央80%に主要要素を収める)を意識して、アイコンの主要要素はそこに収めましょう。
③ Service Workerのキャッシュ戦略(オフライン対応)
vite-plugin-pwaは内部でWorkboxを使ってService Workerを生成します。workboxキーで挙動を細かく制御できます。
vite.config.js
1 | VitePWA({ |
ポイントは3つ。
globPatterns- ビルド成果物のうちプリキャッシュ(インストール時に一括キャッシュ)する対象
cleanupOutdatedCaches: true- 古いバージョンのService Workerが残したキャッシュを自動削除
runtimeCaching- 外部ドメイン(Google Fontsなど)など、ビルド時に分からないリソースの戦略
上の例はGoogle Fontsの戦略をリソース別に分けています。
- stylesheets (CSS) → StaleWhileRevalidate(古くてもまずキャッシュを返して、裏で更新)
- webfonts (woff2) → CacheFirst(フォント実体はほぼ変化しないので長期キャッシュ向き)
戦略選びの目安はこんな感じ。
- CacheFirst: 不変アセット(フォント・ロゴ画像など)。最速・ネットワーク不要
- StaleWhileRevalidate: たまに更新(CSS・スタイル・OGP画像など)。体感は速いまま裏で更新
- NetworkFirst: 鮮度優先(API・記事一覧・通知など)。オフライン時はキャッシュにフォールバック
こぴぺったりはツールなので、NetworkFirstは使用してませんが、必要に応じて使用したほうが良いかもしれません!
ローカルストレージ完結のSPAなら、precache + Google FontsのruntimeCachingだけで十分にオフライン動作します(^^
④ Lighthouseとインストール挙動で確認する
設定が終わったら本番ビルドして検証します。
npm run build && npm run previewで本番ビルドをローカル配信- Chrome DevTools → Lighthouseタブ → Categoriesで「Progressive Web App」を選んで実行
- DevTools → Application → Manifestでアイコン・色がプレビューどおりか目視
- DevTools → Application → Service WorkersにアクティブなSWが登録されているか確認
インストール挙動も実機で確認します。
- PCのChrome: アドレスバー右端に「インストール」アイコン(モニタに⤓マーク)が出る
- Androidスマホ: メニューから「ホーム画面に追加」、または自動で出るインストールバナー
- iOS Safari: 共有メニュー → 「ホーム画面に追加」
npm run devのdevサーバではService Workerが動かないので、PWA挙動の検証はpreviewか本番ドメインで行いましょう。
沼ったところ
古いService Workerが残ってキャッシュが効かない
設定を更新したのに反映されない場合は、古いService Workerがブラウザに残っていることが多いです。
- Chrome DevTools → Application → Service Workers → Unregister
- もしくはApplication → Storage → Clear site data
registerType: "autoUpdate"でも、適用にはリロード2回必要なケースがあります
maskableアイコンを忘れてAndroidで白枠
pwa-maskable-512x512.pngのようなpurpose: "maskable"指定の512×512を1枚追加すれば解消します。
セーフゾーン(中央80%に主要要素を収める)を意識して作ると、円形・角丸・水滴型などどんなマスクをかけられても潰れません。
devサーバでPWA挙動が出ない
devOptions.enabled: false(既定)のとき、npm run dev中はService Workerが登録されません。
これは仕様だそうです。
PWA挙動の確認はnpm run build && npm run previewで行うようにしましょう!
devでPWAも触りたい場合だけdevOptions.enabled: trueにします。
締め
いかがでしたでしょうか?
これをやると結構いい感じに実装できちゃいます。
ここで紹介したことをサクッとまとめると
- vite-plugin-pwaを入れて
registerTypeだけ決めれば、最低限のPWA化は完了 - manifestはホーム追加の見え方を決める設定。アイコンはany + maskableの両対応が必須
- Service WorkerはWorkboxの
runtimeCachingでリソース別に戦略を分ける - 確認は本番ビルド + Lighthouse + 実機ホーム追加
こんな感じで実装したのが、下記のコピー&ペーストを簡単にするツールです。
良ければ使ってみてください!
以上となります。
審査が無くても簡単に上げれるのは便利でよいですね!
それではお疲れさまでした!