【IT】SvelteKitのアプリをPWA化の対応ついて(第2回目:SvelteKitの設定編)
皆さま
こんにちは
本日は、作成したWebアプリをPWA対応します。
今回の環境は、
PC:MacBook Air(M1)
フレームワーク:SvelteKit V5
本記事は、SvelteKitの設定編となります。
・第1回目:事前準備編
・第3回目:動作確認(ローカル、ディプロイ)編
3.SvelteKitの設定
a. パッケージインストール(vite-plugin-pwa)
以下のコマンドでパッケージをインストールします。
(脆弱性もありますが、今回は、リスクLowですので対応は見送ります。)
$ npm install vite-plugin-pwa --save-dev
npm warn deprecated inflight@1.0.6: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm warn deprecated glob@7.2.3: Glob versions prior to v9 are no longer supported
npm warn deprecated sourcemap-codec@1.4.8: Please use @jridgewell/sourcemap-codec instead
added 383 packages, and audited 461 packages in 9s
111 packages are looking for funding
run `npm fund` for details
3 low severity vulnerabilities
To address all issues (including breaking changes), run:
npm audit fix --force
Run `npm audit` for details.b. 必要なアダプターのインストール(@sveltejs/adapter-vercel)
以下のコマンドで必要アダプターインストールします。
buildフォルダー指定で使用します。
※今回は、Vercelを利用しますので@sveltejs/adapter-vercelを利用します。
他のところを利用の場合(static)は、@sveltejs/adapter-staticとなります。
各ベンダーで用意されているものがあればそちらを使用ください)
$ npm install -D @sveltejs/adapter-vercel
up to date, audited 518 packages in 1s
123 packages are looking for funding
run `npm fund` for details
5 low severity vulnerabilities
To address all issues (including breaking changes), run:
npm audit fix --force
Run `npm audit` for details.c. `vite.config.ts` に PWA プラグインを追加
プロジェクト直下の`vite.config.ts`に設定を追加します。
※workbox: {~}は、電波状況が悪い時にしようするキャッシュの設定となります。今回は、電波状況が悪いことを想定して入れてあります。
// Project-Dir/vite.config.ts
import tailwindcss from '@tailwindcss/vite';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
tailwindcss(),
sveltekit(),
VitePWA({
strategies: 'generateSW',
registerType: 'autoUpdate',
injectRegister: 'script',
devOptions: {
enabled: true
},
includeAssets: [
'icons/icon-192.png',
'icons/icon-512.png',
'screenshots/screen1.png',
'screenshots/screen2.png'
],
manifest: {
name: 'アプリ 使用記録',
short_name: 'appname',
start_url: '/',
display: 'standalone',
background_color: '#ffffff',
theme_color: '#0f766e',
lang: 'ja',
icons: [
{
src: '/icons/icon-192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: '/icons/icon-512.png',
sizes: '512x512',
type: 'image/png'
}
],
screenshots: [
{
src: '/screenshots/screen1.png',
sizes: '540x720',
type: 'image/png',
form_factor: 'wide'
},
{
src: '/screenshots/screen2.png',
sizes: '1280x720',
type: 'image/png',
form_factor: 'narrow'
}
]
},
workbox: {
globPatterns: ['**/*.{js,css,ico,png,svg,json,webmanifest}'],
navigateFallback: undefined,
runtimeCaching: [
{
urlPattern: /^\/$/,
handler: 'NetworkFirst'
},
{
urlPattern: /\.(?:png|jpg|jpeg|svg|webp|gif|ico)$/,
handler: 'CacheFirst'
}
]
}
})
],
build: {
cssCodeSplit: true,
rollupOptions: {
output: {
manualChunks(id) {
// node_modules以下はまとめて 'vendor' chunk に
if (id.includes('node_modules')) {
return 'vendor';
}
// Tailwind や Chart.js のスタイルは 'libs' chunk に
if (id.includes('tailwind') || id.includes('chart.js')) {
return 'libs';
}
// その他はデフォルトの分割に任せる
}
}
}
},
esbuild: {
logOverride: { 'this-is-undefined-in-esm': 'silent' }
}
});
d. `src/app.html` にメタタグを追加
src/app.htmlにメタタグを追加します。
※favicon.icoは、favicon.svgを元に作成してstaticフォルダーへ配置ください。
<!-- src/app.html -->
<link rel="manifest" href="/manifest.webmanifest" />
<meta name="theme-color" content="#ffffff" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<!-- icoで旧ブラウザ・fallback対応 -->
<link rel="icon" href="/favicon.ico" type="image/x-icon" /><ご参考>favicon.icoの作成方法
$ magick favicon.svg -define icon:auto-resize=64,48,32,16 favicon.ico
$ identify favicon.ico
favicon.ico[0] ICO 64x64 64x64+0+0 8-bit sRGB 0.000u 0:00.002
favicon.ico[1] ICO 48x48 48x48+0+0 8-bit sRGB 0.000u 0:00.001
favicon.ico[2] ICO 32x32 32x32+0+0 8-bit sRGB 0.000u 0:00.000
favicon.ico[3] ICO 16x16 16x16+0+0 8-bit sRGB 32038B 0.000u 0:00.000e. インストールボタン表示(任意)
src/+layout.svelteにインストール用のボタンを追加します。
Android用となります。
iphoneは、共用ボタンから「ホーム画面に追加」を選択してアイコンを作成ください。
// src/+layout.svelte
<script lang="ts">
import '../app.css';
import { onMount } from 'svelte';
let { children } = $props();
const state = $state({
deferredPrompt: null as Event | null,
showInstallButton: false,
isMobile: false
});
// onMount でSW登録とPWAインストール処理
onMount(async () => {
if (typeof window !== 'undefined') {
// ✅ Service Worker 登録
const { registerSW } = await import('virtual:pwa-register');
registerSW({
immediate: true,
onNeedRefresh() {
console.log('🔄 新しいバージョンがあります');
},
onOfflineReady() {
console.log('✅ オフライン使用の準備ができました');
}
});
// ✅ モバイル判定(タブレット含む)
const ua = navigator.userAgent.toLowerCase();
state.isMobile = /android|iphone|ipad|ipod/.test(ua);
// ✅ Android/PC の PWA インストールイベント
window.addEventListener('beforeinstallprompt', (e) => {
if (!state.isMobile) return; // モバイル以外は無視
e.preventDefault();
state.deferredPrompt = e;
state.showInstallButton = true;
});
}
});
function installApp() {
if (state.deferredPrompt) {
(state.deferredPrompt as any).prompt();
(state.deferredPrompt as any).userChoice.then((choiceResult: any) => {
if (choiceResult.outcome === 'accepted') {
console.log('[PWA] インストール完了');
} else {
console.log('[PWA] ユーザーがキャンセル');
}
state.deferredPrompt = null;
state.showInstallButton = false;
});
}
}
</script>
<!-- ✅ Android モバイルのみ -->
{#if state.showInstallButton}
<div class="fixed bottom-4 left-0 right-0 flex justify-center z-50">
<button
onclick={installApp}
class="bg-teal-600 text-white px-6 py-2 rounded shadow-md hover:bg-teal-700 transition"
>
このアプリをインストール
</button>
</div>
{/if}
{@render children()}
virtual:pwa-registerで警告が出ますので抑止します。
src配下にvirtual-pwa-register.d.tsを配置します。
// src/virtual-pwa-register.d.ts
declare module 'virtual:pwa-register' {
export function registerSW(options?: {
immediate?: boolean;
onNeedRefresh?: () => void;
onOfflineReady?: () => void;
}): void;
}f. ビルド設定
ブロジェクトフォルダー配下の`svelte.config.js`にアダブターの設定をします。(Vercel)
// Project-Dir/svelte.config.js
//import adapter from '@sveltejs/adapter-auto'; //⬅️コメントアウト
import adapter from '@sveltejs/adapter-vercel'; //⬅️追加
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://svelte.dev/docs/kit/integrations
// for more information about preprocessors
preprocess: vitePreprocess(),
kit: {
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
// See https://svelte.dev/docs/kit/adapters for more information about adapters.
adapter: adapter(),
},
};
export default config;
続きは次回へ
では
