PWAをVite+React-tsでお試しした結果

PWAとか仰々しい感じで言われると、「知らないヤツか。。。むずそう」となるけど、意外と開発環境が既に整備されてて、簡単に実装するだけなら拍子抜けするくらい簡単だった。

今開発中のWEBアプリも簡単にネイティブアプリライクにできるし、簡単なWEBアプリくらいならネイティブ開発するよりこれでいいのかもしれない。

PWAとは?(初学者なりに)

PWA(Progressive Web Apps)とは、WEBサイトをホーム画面(アプリ)として実機インストールすることができる機能。ブックマークとかよりもネイティブアプリっぽくできるので、UX向上できる感じ。

Androidでは、ブラウザから既に「ホーム画面に追加」ができるが、あくまでブックマークとしてショートカット的な立ち位置での追加なので、一番の違いはプッシュ通知ができることが一番の違いかなと。

ローカルストレージ枠とかが制限されているらしいので、画像を多量に扱う写真系アプリはネイティブアプリとして出さないときついとかあるかもしれない(調べてない)。

実装

Viteのガイドに従って、Reactのアプリをテンプレートから作成する。

僕は最古からnpm派なので、npm create vite@latest my-vue-app -- --template react-tsで作成

ja.vitejs.dev

PWAのプラグインをインストールする

お使いのパッケージマネージャでvite-plugin-pwaをインストール

インストールアプリの設定をする

Viteプロジェクト作成時にできたvite.config.ts or vite.config.jsにPWAの設定を書き込む

vite.config.ts
export default defineConfig({
  plugins: [
    react(),
// 以下追記
    VitePWA({ 
      registerType: 'autoUpdate',
      injectRegister: 'auto', // ServiceWorker自動生成
      devOptions: { // ローカル環境でデバッグ可に
        enabled: true
      },
      manifest: {
        lang: "ja",
        name: "アプリ名",
        short_name: "アプリ名",
        theme_color: "#000000",
        background_color: "#ffffff",
        display: "standalone", // アプリ起動時のURLバー表示設定
        icons: [ // ないと起動しない
          {
            src: "https://placehold.jp/150x150.png",
            sizes: "150x150",
            type: "image/png"
          }
        ]
      }
    })
  ],
})

インストールのカスタムフックを作成

ここまでで、開発サーバーを立ち上げるなりデプロイするとサイト訪問時にアプリインストールするか聞かれる。もしくは、ブラウザのURLバーのところにインストールボタンが表示される(Chrome/Edge)。が、その出会いは最初の一回だけで、さらにクリックやスワイプですぐに消えてしまうので、インストールボタンを別に作ることを推奨したい。

カスタムフック
import { useState } from 'react'

interface BeforeInstallPromptEvent extends Event {
  readonly platforms: Array<string>;
  readonly userChoice: Promise<{
    outcome: 'accepted' | 'dismissed',
    platform: string
  }>;
  prompt(): Promise<void>;
}

const useInstallApp = () => {
  const [promptEvent, setPromptEvent] = useState<BeforeInstallPromptEvent | null>(null)

  window.addEventListener('beforeinstallprompt', (e: any) => {
    // 自動起動をキャンセル
    e.preventDefault();
    setPromptEvent(e);
  });

  const installApp = () => {
    if (!promptEvent) return
    promptEvent.prompt()
    promptEvent.userChoice.then((choiceResult) => {
      if (choiceResult.outcome === 'accepted') {
        console.log('User accepted the A2HS prompt');
      } else {
        console.log('User dismissed the A2HS prompt');
      }
      setPromptEvent(null)
    });
  }

  return { installApp }
}

export default useInstallApp

使うときは、importしてからinstallApp()をボタンのクッリク等に応じて呼び出すとインストールを促すウィンドウが表示される。