Documentation Vite-PWA
Une Progressive Web App (PWA) est une application qui combine le meilleur du web et du mobile. Elle s'installe sur l'écran d'accueil, fonctionne hors connexion et offre une expérience fluide proche d'une app native.
// vite.config.js
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import tailwindcss from "@tailwindcss/vite";
import { VitePWA } from "vite-plugin-pwa";
import path from "path";
export default defineConfig({
// Réduire les logs en dev
logLevel: 'warn',
// Optimiser les dépendances
optimizeDeps: {
include: [
'prop-types',
'react-signature-canvas',
// Autres dépendances si nécessaire
],
},
// Alias pour les imports
resolve: {
alias: {
'src': path.resolve(__dirname, './src'),
'lib': path.resolve(__dirname, './src/lib'),
},
},
// Plugins
plugins: [
react(),
tailwindcss(),
VitePWA({
// Mise à jour automatique du Service Worker
registerType: 'autoUpdate',
// Configuration Workbox (cache)
workbox: {
// Fichiers à mettre en cache
globPatterns: ['**/*.{js,css,html,ico,png,svg,woff,woff2}'],
// Nettoyer les anciens caches
cleanupOutdatedCaches: true,
// Activer immédiatement le nouveau SW
skipWaiting: true,
},
// Injection automatique du SW
injectRegister: "auto",
// Assets à inclure
includeAssets: [
"favicon.ico",
"favicon.png",
"apple-touch-icon.png",
"assets/*"
],
// Manifest de l'application
manifest: {
name: "Mon Application SmartMaker",
short_name: "MonApp",
description: "Description de mon application",
start_url: "/",
display: "standalone",
background_color: "#ffffff",
theme_color: "#5fbabf",
icons: [
{
src: 'images/pwa-64x64.png',
sizes: '64x64',
type: 'image/png'
},
{
src: 'images/pwa-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: 'images/pwa-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any'
},
{
src: 'images/maskable-icon-512x512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'maskable'
}
],
},
}),
]
});
public/
favicon.ico
favicon.png
apple-touch-icon.png
images/
pwa-64x64.png
pwa-192x192.png
pwa-512x512.png
maskable-icon-512x512.png
<note important>Les images doivent être dans le dossier public/ et les chemins dans le manifest sont relatifs à ce dossier.</note>
| Taille | Usage |
|---|---|
| 64×64 | Favicon |
| 192×192 | Icône Android/Chrome |
| 512×512 | Splash screen, installation |
| 180×180 | Apple Touch Icon (iOS) |
L'icône maskable est adaptée aux différentes formes d'icônes (ronde, carrée, etc.) sur Android. La zone de sécurité est un cercle de 80% centré.
Outils pour générer les icônes :
| Option | Description |
|---|---|
name | Nom complet de l'application |
shortname | URL de démarrage (généralement /) |
display | Mode d'affichage (voir ci-dessous) |
backgroundcolor | Couleur de la barre d'état |
icons | Tableau des icônes |
| Mode | Description |
|---|---|
standalone | Comme une app native (recommandé) |
fullscreen | Plein écran (jeux) |
minimal-ui | Avec contrôles de navigation minimaux |
browser | Dans le navigateur normal |
workbox: {
// Cache-first pour les assets statiques
runtimeCaching: [
{
urlPattern: /^https:\/\/fonts\.googleapis\.com\/.*/i,
handler: 'CacheFirst',
options: {
cacheName: 'google-fonts-cache',
expiration: {
maxEntries: 10,
maxAgeSeconds: 60 * 60 * 24 * 365 // 1 an
},
cacheableResponse: {
statuses: [0, 200]
}
}
},
{
// Network-first pour l'API
urlPattern: /^https:\/\/api\.example\.com\/.*/i,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 60 * 60 // 1 heure
},
networkTimeoutSeconds: 10
}
}
]
}
| Stratégie | Usage |
|---|---|
CacheFirst | Assets statiques (fonts, images) |
NetworkFirst | API, données dynamiques |
StaleWhileRevalidate | Contenu qui peut être légèrement obsolète |
NetworkOnly | Toujours réseau (authentification) |
CacheOnly | Uniquement cache (assets embarqués) |
import { useEffect, useState } from 'react';
export const useOnlineStatus = () => {
const [isOnline, setIsOnline] = useState(navigator.onLine);
useEffect(() => {
const handleOnline = () => setIsOnline(true);
const handleOffline = () => setIsOnline(false);
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
};
import { useOnlineStatus } from './hooks/useOnlineStatus';
const App = () => {
const isOnline = useOnlineStatus();
return (
<div>
{!isOnline && (
<div className="bg-yellow-500 text-white p-2 text-center">
Mode hors connexion
</div>
)}
{/* ... */}
</div>
);
};
Utilisez useDb pour stocker les données localement et synchroniser quand la connexion revient :
import { useDb } from '@cap-rel/smartcommon';
const db = useDb({
name: 'myApp',
stores: {
pendingSync: 'id++, action, data, createdAt'
}
});
// Stocker une action en attente
const saveOffline = async (action, data) => {
await db.pendingSync.add({
action,
data,
createdAt: Date.now()
});
};
// Synchroniser quand en ligne
const syncPending = async (api) => {
const pending = await db.pendingSync.toArray();
for (const item of pending) {
try {
await api.private.post(item.action, { json: item.data });
await db.pendingSync.delete(item.id);
} catch (error) {
console.error('Sync failed:', error);
}
}
};
# Build de production npm run build # Prévisualiser le build npm run preview # Le build génère: # - dist/index.html # - dist/assets/*.js # - dist/assets/*.css # - dist/sw.js (Service Worker) # - dist/manifest.webmanifest
Après le build, vérifiez dans les DevTools :
Copiez le contenu de dist/ dans le dossier pwa/ de votre module Dolibarr :
# Depuis le dossier mobile/ npm run build cp -r dist/* ../pwa/
Ou utilisez le Makefile fourni par SmartBoot :
make pwa