Patrons Vite
Patrons d'outils de build et de serveur de développement pour les projets Vite 8+. Couvre la configuration, les variables d'environnement, la configuration du proxy, le mode librairie, le pré-regroupement des dépendances et les pièges de production courants.
Quand utiliser
- Configurer
vite.config.tsouvite.config.js - Mettre en place des variables d'environnement ou des fichiers
.env - Configurer le proxy du serveur de développement pour les backends API
- Optimiser la sortie du build (chunks, minification, ressources)
- Publier des librairies avec
build.lib - Résoudre les problèmes de pré-regroupement des dépendances ou d'interopérabilité CJS/ESM
- Déboguer les erreurs HMR, de serveur de développement ou de build
- Choisir ou ordonner les plugins Vite
Comment ça fonctionne
- Le mode dev sert les fichiers sources en tant qu'ESM natif — sans regroupement. Les transformations ont lieu à la demande pour chaque requête de module, ce qui explique les démarrages à froid rapides et le HMR précis.
- Le mode build utilise Rolldown (v7+) ou Rollup (v5–v6) pour regrouper l'application pour la production avec élagage, division de code et minification basée sur Oxc.
- Le pré-regroupement des dépendances convertit les dépendances CJS/UMD en ESM une fois via esbuild et met en cache le résultat sous
node_modules/.vite, de sorte que les démarrages suivants évitent ce travail. - Les plugins partagent une interface unifiée entre le dev et le build — le même objet plugin fonctionne à la fois pour les transformations à la demande du serveur de développement et pour le pipeline de production.
- Les variables d'environnement sont injectées statiquement au moment du build. Les variables préfixées par
VITE_deviennent des constantes publiques dans le bundle ; tout ce qui n'est pas préfixé est invisible pour le code client.
Exemples
Structure de configuration
Configuration de base
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
resolve: {
alias: { '@': new URL('./src', import.meta.url).pathname },
},
})
Configuration conditionnelle
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig(({ command, mode }) => {
const env = loadEnv(mode, process.cwd()) // préfixé par VITE_ uniquement (sûr)
return {
plugins: [react()],
server: command === 'serve' ? { port: 3000 } : undefined,
define: {
__API_URL__: JSON.stringify(env.VITE_API_URL),
},
}
})
Options de configuration clés
| Clé | Défaut | Description |
|---|---|---|
root | '.' | Racine du projet (où se trouve index.html) |
base | '/' | Chemin de base public pour les ressources déployées |
envPrefix | 'VITE_' | Préfixe pour les variables d'environnement exposées au client |
build.outDir | 'dist' | Répertoire de sortie |
build.minify | 'oxc' | Minificateur ('oxc', 'terser' ou false) |
build.sourcemap | false | true, 'inline' ou 'hidden' |
Plugins
Plugins essentiels
La plupart des besoins en plugins sont couverts par une poignée de packages bien maintenus. Utilisez-les avant d'écrire les vôtres.
| Plugin | Objectif | Quand utiliser |
|---|---|---|
@vitejs/plugin-react-swc | React HMR + Fast Refresh via SWC | Par défaut pour les applis React (plus rapide que la variante Babel) |
@vitejs/plugin-react | React HMR + Fast Refresh via Babel | Seulement si vous avez besoin de plugins Babel (emotion, décorateurs MobX) |
@vitejs/plugin-vue | Support Vue 3 SFC | Applis Vue |
vite-plugin-checker | Exécute tsc + ESLint dans un thread worker avec superposition HMR | Toute appli TypeScript — Vite ne fait PAS de vérification de type pendant vite build |
vite-tsconfig-paths | Respecte les alias de chemins tsconfig.json | Quand vous avez déjà des alias dans tsconfig.json |
vite-plugin-dts | Émet des fichiers .d.ts en mode librairie | Publication de librairies TypeScript |
vite-plugin-svgr | Importe les SVG en tant que composants React | Applis React utilisant des SVG comme composants |
rollup-plugin-visualizer | Rapport treemap/sunburst du bundle | Audits périodiques de la taille du bundle (utiliser enforce: 'post') |
vite-plugin-pwa | PWA sans configuration + Workbox | Applis fonctionnant hors ligne |
Avertissement critique : vite build transpile mais ne fait PAS de vérification de type. Les erreurs de type sont déployées en production silencieusement, sauf si vous ajoutez vite-plugin-checker ou exécutez tsc --noEmit dans le CI.
Créer des plugins personnalisés
La création est rare — la plupart des besoins sont couverts par des plugins existants. Quand vous en avez besoin, commencez en ligne dans vite.config.ts et extrayez seulement si réutilisé.
// vite.config.ts — plugin en ligne minimal
function monPlugin(): Plugin {
return {
name: 'mon-plugin', // obligatoire, doit être unique
enforce: 'pre', // 'pre' | 'post' (optionnel)
apply: 'build', // 'build' | 'serve' (optionnel)
transform(code, id) {
if (!id.endsWith('.custom')) return
return { code: transformCustom(code), map: null }
},
}
}
Hooks clés : transform (modifier la source), resolveId + load (modules virtuels), transformIndexHtml (injecter dans le HTML), configureServer (ajouter un middleware de dev), hotUpdate (HMR personnalisé — remplace handleHotUpdate déprécié dans v7+).
Modules virtuels utilisent la convention de préfixe \0 — resolveId retourne '\0virtual:mon-id' pour que les autres plugins l'ignorent. Le code utilisateur importe 'virtual:mon-id'.
Pour l'API complète des plugins, voir vite.dev/guide/api-plugin. Utilisez vite-plugin-inspect pendant le développement pour déboguer le pipeline de transformation.
API HMR
Les plugins de framework (@vitejs/plugin-react, @vitejs/plugin-vue, etc.) gèrent le HMR automatiquement. Utilisez import.meta.hot directement uniquement lors de la création de stores d'état personnalisés, d'outils de développement ou d'utilitaires agnostiques au framework qui doivent persister l'état entre les mises à jour.
// src/store.ts — HMR manuel pour un module vanilla
if (import.meta.hot) {
// Persister l'état entre les mises à jour (doit MUTER, ne jamais réaffecter .data)
import.meta.hot.data.count = import.meta.hot.data.count ?? 0
// Nettoyer les effets de bord avant que le module ne soit remplacé
import.meta.hot.dispose((data) => clearInterval(data.intervalId))
// Accepter les mises à jour de ce module lui-même
import.meta.hot.accept()
}
Tout le code import.meta.hot est élagué des builds de production — aucun retrait de garde nécessaire.
Variables d'environnement
Vite charge .env, .env.local, .env.[mode] et .env.[mode].local dans cet ordre (les derniers écrasent les précédents) ; les fichiers *.local sont ignorés par git et destinés aux secrets locaux.
Accès côté client
Seules les variables préfixées par VITE_ sont exposées au code client :
import.meta.env.VITE_API_URL // chaîne
import.meta.env.MODE // 'development' | 'production' | personnalisé
import.meta.env.BASE_URL // valeur de la config base
import.meta.env.DEV // booléen
import.meta.env.PROD // booléen
import.meta.env.SSR // booléen
Utiliser les variables d'env dans la config
// vite.config.ts
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd()) // préfixé par VITE_ uniquement (sûr)
return {
define: {
__API_URL__: JSON.stringify(env.VITE_API_URL),
},
}
})
Sécurité
Le préfixe VITE_ n'est PAS une frontière de sécurité
Toute variable préfixée par VITE_ est injectée statiquement dans le bundle client au moment du build. La minification, l'encodage base64 et la désactivation des sourcemaps ne la cachent PAS. Un attaquant déterminé peut extraire n'importe quelle variable VITE_ du JavaScript livré.
Règle : Seules les valeurs publiques (URLs d'API, indicateurs de fonctionnalités, clés publiques) vont dans les variables VITE_. Les secrets (jetons d'API, URLs de base de données, clés privées) DOIVENT vivre côté serveur derrière une API ou une fonction sans serveur.
Le piège de loadEnv('')
// MAUVAIS : passer '' comme troisième argument charge TOUTES les variables d'env — y compris les secrets serveur —
// et les rend disponibles pour être injectées dans le code client via `define`.
const env = loadEnv(mode, process.cwd(), '')
// BON : liste explicite des préfixes
const env = loadEnv(mode, process.cwd(), ['VITE_', 'APP_'])
Sourcemaps en production
Les sourcemaps de production divulguent votre code source original. Désactivez-les sauf si vous les téléversez vers un traqueur d'erreurs (Sentry, Bugsnag) et les supprimez localement ensuite :
build: {
sourcemap: false, // défaut — gardez-le ainsi
}
Checklist .gitignore
.env.local,.env.*.local— écrasements de secrets locauxdist/— sortie du buildnode_modules/.vite— cache de pré-regroupement (les entrées obsolètes causent des erreurs fantômes)
Proxy serveur
// vite.config.ts — server.proxy
server: {
proxy: {
'/foo': 'http://localhost:4567', // raccourci chaîne
'/api': {
target: 'http://localhost:8080',
changeOrigin: true, // nécessaire pour les backends à hébergement virtuel
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
}
Pour le proxy WebSocket, ajoutez ws: true à la configuration de la route.
Optimisation du build
Chunks manuels
// vite.config.ts — build.rolldownOptions
build: {
rolldownOptions: {
output: {
// Forme objet : grouper des packages spécifiques
manualChunks: {
'react-vendor': ['react', 'react-dom'],
'ui-vendor': ['@radix-ui/react-dialog', '@radix-ui/react-popover'],
},
},
},
}
// Forme fonction : diviser par heuristique
manualChunks(id) {
if (id.includes('node_modules/react')) return 'react-vendor'
if (id.includes('node_modules')) return 'vendor'
}
Performance
Éviter les fichiers barils
Les fichiers barils (index.ts réexportant tout d'un répertoire) forcent Vite à charger chaque fichier réexporté même lorsque vous importez un seul symbole. C'est le #1 des ralentissements du serveur de développement signalé par la documentation officielle.
// MAUVAIS — importer un utilitaire force Vite à charger tout le baril
import { slash } from '@/utils'
// BON — import direct, seul le fichier est chargé
import { slash } from '@/utils/slash'
Être explicite avec les extensions d'import
Chaque extension implicite force jusqu'à 6 vérifications du système de fichiers via resolve.extensions. Dans les grandes bases de code, cela s'additionne.
// MAUVAIS
import Component from './Component'
// BON
import Component from './Component.tsx'
Réduisez tsconfig.json allowImportingTsExtensions + resolve.extensions aux seules extensions que vous utilisez réellement.
Préchauffer les routes à fort trafic
server.warmup.clientFiles pré-transforme les entrées connues avant que le navigateur ne les demande — éliminant la cascade de requêtes de chargement à froid sur les grandes applications.
// vite.config.ts
server: {
warmup: {
clientFiles: ['./src/main.tsx', './src/routes/**/*.tsx'],
},
}
Profilage des serveurs de développement lents
Quand vite dev semble lent, commencez par vite --profile, interagissez avec l'application, puis appuyez sur p+entrée pour enregistrer un .cpuprofile. Chargez-le dans Speedscope pour trouver quels plugins consomment du temps — généralement les hooks buildStart, config ou configResolved dans les plugins communautaires.
Mode librairie
Lors de la publication d'un package npm, utilisez build.lib. Deux pièges sont plus importants que le détail de la configuration :
- Les types ne sont pas émis — ajoutez
vite-plugin-dtsou exécuteztsc --emitDeclarationOnlyséparément. - Les dépendances de pair DOIVENT être externalisées — les pairs non listés sont regroupés dans votre librairie, provoquant des erreurs de double runtime chez les consommateurs.
// vite.config.ts
build: {
lib: {
entry: 'src/index.ts',
formats: ['es', 'cjs'],
fileName: (format) => `ma-lib.${format}.js`,
},
rolldownOptions: {
external: ['react', 'react-dom', 'react/jsx-runtime'], // chaque dépendance de pair
},
}
Externalisations SSR
Les configurations createServer({ middlewareMode: true }) nues sont du ressort des auteurs de framework. La plupart des applications devraient utiliser Nuxt, Remix, SvelteKit, Astro ou TanStack Start à la place. Ce que vous ajusterez en tant qu'utilisateur de framework, c'est la configuration des externalisations lorsque des dépendances cassent en SSR :
// vite.config.ts — options ssr
ssr: {
external: ['package-natif-node'], // garder en tant que require() dans le bundle SSR
noExternal: ['package-uniquement-esm'], // forcer le regroupement dans la sortie SSR (corrige la plupart des erreurs SSR)
target: 'node', // 'node' ou 'webworker'
}
Pré-regroupement des dépendances
Vite pré-regroupe les dépendances pour convertir CJS/UMD en ESM et réduire le nombre de requêtes.
// vite.config.ts — optimizeDeps
optimizeDeps: {
include: [
'lodash-es', // forcer le pré-regroupement des dépendances lourdes connues
'package-cjs', // dépendances CJS qui causent des problèmes d'interopérabilité
'lib-profonde/composants/**', // glob pour les imports profonds
],
exclude: ['package-esm-local'], // doit être un ESM valide si exclu
force: true, // ignorer le cache, ré-optimiser (débogage temporaire)
}
Pièges courants
Le dev ne correspond pas au build
Le dev utilise esbuild/Rolldown pour les transformations ; le build utilise Rolldown pour le regroupement. Les librairies CJS peuvent se comporter différemment entre les deux. Vérifiez toujours avec vite build && vite preview avant de déployer.
Chunks obsolètes après le déploiement
Les nouveaux builds produisent de nouveaux hachages de chunks. Les utilisateurs avec des sessions actives demandent des noms de fichiers anciens qui n'existent plus. Vite n'a pas de solution intégrée. Atténuations :
- Gardez les anciens fichiers
dist/assets/actifs pendant une fenêtre de déploiement - Attrapez les erreurs d'import dynamique dans votre routeur et forcez un rechargement de page
Docker et conteneurs
Vite se lie à localhost par défaut, ce qui est inaccessible depuis l'extérieur d'un conteneur :
// vite.config.ts — configuration Docker/conteneur
server: {
host: true, // lier 0.0.0.0
hmr: { clientPort: 3000 }, // si derrière un proxy inverse
}
Accès aux fichiers dans les monorepos
Vite restreint le service de fichiers à la racine du projet. Les packages en dehors de la racine sont bloqués :
// vite.config.ts — accès aux fichiers monorepo
server: {
fs: {
allow: ['..'], // autoriser le répertoire parent (racine de l'espace de travail)
},
}
Anti-patrons
// MAUVAIS : Définir envPrefix à '' expose TOUTES les variables d'environnement (y compris les secrets) au client
envPrefix: ''
// MAUVAIS : Supposer que require() fonctionne dans ...