Étape 26 — Transformer le Pokédex en PWA
Accessible à tout le monde
Cette étape fonctionne sur Windows, macOS et Linux et ne nécessite aucun outil supplémentaire : juste du code et le navigateur. Le résultat est testable en local en 30 minutes.
Temps estimé
Environ 30 minutes : 5 min d'install, 10 min de config, 5 min de tests, 10 min pour comprendre le service worker.
Objectif
Transformer votre Pokédex (actuellement une SPA) en une PWA (Progressive Web App). Concrètement, ça veut dire :
- Installable sur l'écran d'accueil (mobile et desktop)
- Fonctionne hors ligne une fois visité une fois (les ressources sont en cache)
- Icône d'app dans le launcher avec nom personnalisé
- Chargement instantané après la première visite (cache)
Pour rappel (voir E25)
Une PWA = une SPA + un manifest (métadonnées d'installation) + un service worker (cache et offline). HTTPS obligatoire en production (localhost compte comme sécurisé).
Pré-requis
- L'étape E25 lue (vous comprenez la différence SPA / PWA)
- Le projet Pokédex Vuetify fonctionnel localement (
npm run devOK) - Une connexion internet (pour
npm install)
Tâches
1. Installer le plugin Vite PWA
npm install --save-dev vite-plugin-pwaCe plugin officiel (vite-pwa-org.netlify.app) génère automatiquement le service worker, le manifest, et un fichier de précache de tous vos assets.
2. Configurer le plugin dans vite.config.mjs
Ouvrez vite.config.mjs à la racine du projet et ajoutez le plugin :
// Plugins
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import Fonts from 'unplugin-fonts/vite'
import Layouts from 'vite-plugin-vue-layouts'
import { VitePWA } from 'vite-plugin-pwa' // ← AJOUT
import Vue from '@vitejs/plugin-vue'
import VueRouter from 'unplugin-vue-router/vite'
import Vuetify, { transformAssetUrls } from 'vite-plugin-vuetify'
// ...
export default defineConfig({
base: process.env.VITE_BASE_URL || './',
plugins: [
VueRouter(),
Layouts(),
Vue({ template: { transformAssetUrls } }),
Vuetify({
autoImport: true,
styles: { configFile: 'src/styles/settings.scss' },
}),
Components(),
Fonts({
google: {
families: [{ name: 'Roboto', styles: 'wght@100;300;400;500;700;900' }],
},
}),
AutoImport({
imports: ['vue', 'vue-router'],
eslintrc: { enabled: true },
vueTemplate: true,
}),
// ↓↓↓ Configuration PWA ↓↓↓
VitePWA({
// 'autoUpdate' : le service worker met à jour le cache automatiquement
// dès qu'un nouveau build est déployé (recommandé pour les débutants).
registerType: 'autoUpdate',
// Pour tester en dev avec npm run dev (sans build)
devOptions: {
enabled: true,
},
// Manifest : métadonnées d'installation
// (nom de l'app, icônes, couleur de la barre de statut, etc.)
manifest: {
name: 'Pokédex Vuetify',
short_name: 'Pokédex',
description: 'Encyclopédie Pokémon construite avec Vue.js et Vuetify',
theme_color: '#121212',
background_color: '#121212',
display: 'standalone',
orientation: 'portrait',
scope: '/',
start_url: '/',
icons: [
{
src: '/icons/pwa-192x192.png',
sizes: '192x192',
type: 'image/png',
},
{
src: '/icons/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
},
{
src: '/icons/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'maskable',
},
],
},
// Stratégie de cache : tout ce qui est dans dist/ est mis en cache
// au premier chargement (= app utilisable hors ligne ensuite).
workbox: {
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2}'],
// Cache des appels à l'API Vercel (1 jour)
runtimeCaching: [
{
urlPattern: /^https:\/\/2025-sfa-pokedex-api\.vercel\.app\/.*/i,
handler: 'NetworkFirst',
options: {
cacheName: 'pokedex-api-cache',
expiration: {
maxEntries: 50,
maxAgeSeconds: 60 * 60 * 24, // 24h
},
},
},
],
},
}),
// ↑↑↑ Fin de la configuration PWA ↑↑↑
],
// ... reste de la config inchangé
})Stratégies de cache (runtimeCaching)
- NetworkFirst : essaie le réseau, fallback cache si hors ligne. Bon pour les API.
- CacheFirst : sert le cache, fallback réseau. Bon pour les images, fonts.
- StaleWhileRevalidate : sert le cache + met à jour en arrière-plan. Bon compromis.
3. Créer les icônes d'app
La PWA a besoin d'icônes 192×192 et 512×512 (minimum). Créez le dossier public/icons/ et placez-y deux PNG carrés :
public/
├── favicon.ico
└── icons/
├── pwa-192x192.png ← 192 × 192 pixels
└── pwa-512x512.png ← 512 × 512 pixelsGénérer les icônes automatiquement
Si vous avez une icône source carrée (au moins 512×512), utilisez le site maskable.app/editor pour générer toutes les tailles. Pour aller plus vite, le package @vite-pwa/assets-generator automatise tout :
npm install --save-dev @vite-pwa/assets-generator
npx pwa-assets-generator --preset minimal public/source-icon.pngPour ce cours, vous pouvez télécharger une pokéball icones8.com/icon/sjEvO87Wmh32/pokeball et la redimensionner.
4. Builder et tester
npm run build
npm run preview
# → ouvrir http://localhost:4173npm run preview est obligatoire pour tester la PWA
Le service worker ne se charge pas avec npm run dev (sauf si devOptions.enabled: true comme dans notre config). Pour un test réaliste, utilisez toujours preview qui sert la version buildée.
5. Vérifier l'installation PWA
Dans Brave / Chrome / Firefox, ouvrez DevTools → onglet Application :
- Manifest : vérifier le nom, les icônes, theme color
- Service Workers : confirmer qu'il est
activated and running - Cache Storage : vérifier qu'un cache
workbox-precache-v2existe avec tous les fichiers
Dans la barre d'adresse, cliquer sur l'icône Installer (à côté du favicon — disponible quand la PWA est valide). Le Pokédex s'installe comme une app desktop avec sa propre fenêtre et son icône dans le launcher.
6. Tester le hors ligne
Toujours dans DevTools, onglet Network :
- Cocher Offline (en haut)
- Rafraîchir la page (Cmd/Ctrl + R)
- L'app continue de fonctionner — vous voyez la dernière liste de Pokémon mise en cache, et vous pouvez naviguer entre les pages
Premières interactions hors ligne uniquement
La PWA sert le cache existant. Vous ne pouvez pas créer un nouveau Pokémon hors ligne (l'appel POST à l'API échouera). Pour ça il faudrait implémenter un système de synchronisation différée (workbox-background-sync) — sortir du scope de cette étape.
7. Auditer avec Lighthouse
Dans DevTools → onglet Lighthouse :
- Choisir Mode : Navigation et Catégorie : PWA
- Cliquer Analyser
- Vous devriez obtenir un score PWA de 100/100 (ou très proche)
Si certains critères sont rouges (par exemple "Manifest doesn't have a maskable icon"), corrigez selon les recommandations.
Tests à effectuer
- L'app charge sans erreur en
npm run preview - DevTools → Application → Service Workers : activated and running
- DevTools → Application → Manifest : nom, icônes, theme color OK
- L'icône Installer apparaît dans la barre d'adresse
- Une fois installée, l'app a sa propre fenêtre (pas d'onglet navigateur)
- Mode offline (DevTools → Network) : l'app continue de fonctionner
- Lighthouse audit PWA : score > 90
Pièges connus
HTTPS obligatoire en production
Sur Vercel, Netlify, GitHub Pages : HTTPS automatique, tout marche. Sur un serveur custom : il faut un certificat SSL (Let's Encrypt). En localhost : exception, HTTP fonctionne.
Mise à jour du service worker
Si vous modifiez le code et rebuilder, le service worker des utilisateurs ne se met à jour qu'au prochain chargement. Avec registerType: 'autoUpdate', ça marche automatiquement mais il peut y avoir un délai d'une visite.
Pour forcer un refresh : DevTools → Application → Service Workers → "Unregister" + rafraîchir.
Apple Safari iOS : restrictions
Safari sur iOS supporte les PWA mais avec des limitations :
- Pas d'installation automatique via icône dans la barre — il faut Partager → Sur l'écran d'accueil
- Pas de notifications push (sauf iOS 16.4+)
- Cache limité à 50 Mo (vs ~1 Go sur Chrome Android)
Pour un vrai effet "app native" sur iPhone, utilisez plutôt Capacitor (étapes E27 à E29).
Commit
git add -A
git commit -m "feat(pwa): transformer le Pokédex en PWA installable"Récapitulatif visuel
Avant cette étape : votre Pokédex était une SPA classique consultable uniquement via navigateur, sans installation possible, ne marche pas hors ligne.
Après cette étape : votre Pokédex est installable comme une app sur mobile et desktop, fonctionne hors ligne, et se met à jour automatiquement.
Suite
Vous avez maintenant une PWA fonctionnelle. Vous pouvez :
- Vous arrêter ici si votre objectif est uniquement "web installable + hors ligne"
- Continuer vers le mobile natif avec E27 (Capacitor + Android) pour publier sur le Play Store et obtenir des perfs vraiment natives
PWA vs Capacitor — quel choix ?
| Critère | PWA | Capacitor |
|---|---|---|
| Effort | 30 min | 1 à 3 heures |
| Installation | Via navigateur | Via App Store / Play Store |
| Compte développeur | Gratuit | 99 USD/an Apple, 25 USD une fois Google |
| API natives (caméra, contacts, push) | Limitées | Complètes |
| Visibilité (stores) | Non | Oui |
| Recommandation | App interne, MVP rapide | App publique, monétisable |