Développement Web

Vite
Build Tool Ultra-Rapide

Dev server instantané basé sur les ES modules natifs, HMR ultra-rapide, build production avec Rollup, plugins, Vitest — tout ce qu'il faut pour un workflow moderne.

Pourquoi Vite ?

Vite (« vite » en français) est un outil de build créé par Evan You (créateur de Vue). Sa différence fondamentale : en développement, il ne bundle pas le code — il sert les fichiers directement en ES modules natifs, ce qui rend le démarrage instantané, quelle que soit la taille du projet.

Vite (démarrage)
< 300ms
Vite (HMR)
< 50ms
Webpack (démarrage)
20–60s
Webpack (HMR)
2–10s
CRA (démarrage)
15–30s
Pourquoi c'est si rapide ?
Dev server traditionnel (Webpack/CRA) :
┌─ Lire tous les fichiers
├─ Parser les imports
├─ Transformer (Babel, TS, CSS…)
├─ Bundler en un gros fichier
└─ Servir → 20-60 secondes !

Vite en développement :
┌─ Démarrer le serveur (instantané)
├─ Pré-bundler seulement les node_modules
│   avec esbuild (10-100× plus vite que Babel)
└─ Servir les fichiers source DIRECTEMENT
   Le navigateur résout les imports lui-même
   → < 300ms !

HMR (Hot Module Replacement) :
Webpack : rebundle → envoie tout au navigateur
Vite    : envoie SEULEMENT le module modifié
→ 50ms quelle que soit la taille du projet

Build de production :
Vite utilise Rollup (stable, mature, tree-shaking)
→ bundles optimisés, code-splitting automatique

Démarrage rapide

Créer un projet Vite
# Interactive — choisir le template
npm create vite@latest

# Avec template direct
npm create vite@latest mon-app -- --template react
npm create vite@latest mon-app -- --template react-ts
npm create vite@latest mon-app -- --template vue
npm create vite@latest mon-app -- --template vue-ts
npm create vite@latest mon-app -- --template svelte
npm create vite@latest mon-app -- --template vanilla-ts
npm create vite@latest mon-app -- --template lit

cd mon-app
npm install
npm run dev     # → http://localhost:5173

# Scripts disponibles (package.json)
{
  "scripts": {
    "dev":     "vite",           ← dev server
    "build":   "vite build",     ← production build
    "preview": "vite preview",   ← tester le build local
    "lint":    "eslint ."
  }
}

# Avec yarn / pnpm
yarn create vite mon-app --template react-ts
pnpm create vite mon-app --template react-ts
Templates disponibles
Templates officiels :
vanilla          vanilla-ts
vue              vue-ts
react            react-ts
react-swc        react-swc-ts     ← plus rapide
preact           preact-ts
lit              lit-ts
svelte           svelte-ts
solid            solid-ts
qwik             qwik-ts

react-swc vs react :
→ swc = Speedy Web Compiler (Rust)
→ Remplace Babel pour les transforms
→ 10-20× plus rapide que Babel
→ Recommandé pour les nouveaux projets

Starters communautaires (via degit) :
npm create vite-extra@latest   ← templates extras
Ex : vite + react + tailwind + vitest
degit user/repo mon-app

Structure & vite.config.ts

Structure d'un projet Vite+React
mon-app/
├── public/              ← fichiers copiés tels quels dans dist/
│   └── favicon.ico         référencés avec / (ex: /favicon.ico)
│
├── src/
│   ├── assets/          ← fichiers importés → traités par Vite
│   │   └── logo.svg        (hachage, optimisation)
│   ├── components/
│   ├── App.tsx
│   └── main.tsx         ← point d'entrée
│
├── index.html           ← RACINE du projet (pas dans public/)
├── vite.config.ts
├── tsconfig.json
└── package.json

index.html — point d'entrée Vite
<!DOCTYPE html>
<html>
  <body>
    <div id="root"></div>
    <!-- Vite injecte le script automatiquement -->
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>
vite.config.ts — configuration complète
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react-swc';
import { resolve } from 'path';

export default defineConfig({
  plugins: [react()],

  // Alias de chemins
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
      '@components': resolve(__dirname, 'src/components'),
    },
  },

  // Serveur de développement
  server: {
    port: 3000,
    open: true,           // ouvrir le navigateur
    host: true,           // accès réseau local
    proxy: {              // proxy API
      '/api': 'http://localhost:8080'
    }
  },

  // Build de production
  build: {
    outDir: 'dist',
    sourcemap: true,
    minify: 'terser',
  },

  // Tests Vitest
  test: {
    globals: true,
    environment: 'jsdom',
  },
});

Dev server & HMR

Options du serveur de développement
server: {
  port:   3000,     // port (défaut: 5173)
  open:   true,     // ouvrir le navigateur
  host:   '0.0.0.0', // accès réseau (mobile, VM)
  https:  true,     // HTTPS avec certificat auto
  strictPort: true, // erreur si port déjà utilisé

  cors:   true,     // CORS pour les requêtes fetch

  // Hmr — Hot Module Replacement
  hmr: {
    overlay: true,      // afficher les erreurs dans le navigateur
    port:    24678,     // port WebSocket HMR
    host:    'localhost',
  },

  // Surveillance des fichiers
  watch: {
    ignored: ['**/node_modules/**', '**/dist/**'],
  }
}

# Raccourcis dans le terminal Vite
r → forcer le reload complet
u → afficher/masquer l'URL serveur
o → ouvrir dans le navigateur
c → effacer la console
q → quitter
Modules ES natifs — comprendre le fonctionnement
// En dev, chaque import = une requête HTTP au serveur Vite
// Le navigateur résout lui-même le graphe de dépendances

// main.tsx
import React from 'react'       // → GET /node_modules/.vite/react.js
import App from './App'        // → GET /src/App.tsx (transformé)
import styles from './App.css' // → GET /src/App.css (injecté JS)

// node_modules = pré-bundlés avec esbuild (1 seule fois)
// Stockés dans node_modules/.vite/deps/
// Recalculés seulement si package.json change

// HMR — comment ça marche
// 1. Vous sauvegardez App.tsx
// 2. Vite détecte le changement
// 3. Re-transforme SEULEMENT App.tsx
// 4. Envoie le module via WebSocket
// 5. Le navigateur remplace le module en mémoire
// 6. React Fast Refresh préserve le state

// API HMR manuelle (rarement nécessaire)
if (import.meta.hot) {
  import.meta.hot.accept('./dep', (newMod) => {
    // re-initialiser avec le nouveau module
  });
  import.meta.hot.dispose(() => {
    // nettoyage avant remplacement
  });
}

Gestion des assets

Importer des assets
// Images — retourne l'URL transformée
import logoUrl from './assets/logo.png';
import logoSvg from './assets/logo.svg';
return <img src={logoUrl} alt="Logo" />;

// SVG comme composant React (avec plugin)
import { ReactComponent as Logo } from './logo.svg?react';
return <Logo width={100} />;

// URL explicite
import url from './fichier.pdf?url';    // toujours une URL
import raw from './data.txt?raw';     // contenu en string

// Inline (pour les petits fichiers)
import iconUrl from './icon.svg?inline'; // data URI

// Worker Web
import Worker from './worker.ts?worker';
const worker = new Worker();

// WASM
import init from './module.wasm?init';
await init();

// JSON — tree-shaking natif
import config from './config.json';
import { version } from './package.json'; // partiel
public/ vs assets/
public/
→ Fichiers COPIÉS tels quels dans dist/
→ Accessibles via URL absolue : /logo.png
→ Pas de hachage, pas de transformation
→ Usage : favicon, robots.txt, manifest.json,
  fichiers référencés dans du code non-bundlé

src/assets/
→ Fichiers IMPORTÉS dans le code
→ Traités par Vite (hachage, optimisation)
→ URL avec hash : /assets/logo-D1a2b3c4.png
→ Usage : images de l'app, fonts, SVG

Limite d'inline automatique :
build: {
  assetsInlineLimit: 4096  // 4KB → data URI
}
// Fichiers < 4KB → intégrés en base64 (moins de requêtes)
// Fichiers > 4KB → copiés dans assets/ avec hash

Fonts — importer depuis src/assets/
/* styles.css */
@font-face {
  font-family: 'MyFont';
  src: url('./assets/fonts/MyFont.woff2') format('woff2');
}

Variables d'environnement

Fichiers .env
# Fichiers chargés selon l'environnement :
.env                  ← toujours chargé
.env.local            ← toujours, ignoré par git
.env.development      ← npm run dev seulement
.env.production       ← npm run build seulement
.env.test             ← vitest seulement
.env.development.local ← dev + ignoré par git

# Variables dans .env
# OBLIGATOIRE : préfixe VITE_ pour être exposé au client
VITE_API_URL=https://api.example.com
VITE_APP_TITLE=Mon Application
VITE_FEATURE_FLAG=true

# Sans préfixe = serveur seulement (non exposé)
DB_PASSWORD=secret123   ← JAMAIS accessible côté client

# .gitignore — NE JAMAIS commiter les .env.local
.env.local
.env.*.local
Utiliser les variables
// Accès via import.meta.env
const apiUrl = import.meta.env.VITE_API_URL;
const title  = import.meta.env.VITE_APP_TITLE;

// Variables intégrées par Vite :
import.meta.env.MODE      // 'development' | 'production' | 'test'
import.meta.env.DEV       // true en dev
import.meta.env.PROD      // true en prod
import.meta.env.SSR       // true si SSR
import.meta.env.BASE_URL  // valeur de base (config base:)

// Conditionnement selon l'env
if (import.meta.env.DEV) {
  console.log('Mode développement');
}

// Code éliminé en production (tree-shaken)
if (import.meta.env.PROD) {
  // Analytiques, Sentry, etc.
  initSentry(import.meta.env.VITE_SENTRY_DSN);
}

// TypeScript — typer import.meta.env
// vite-env.d.ts
interface ImportMetaEnv {
  readonly VITE_API_URL: string;
  readonly VITE_APP_TITLE: string;
}
interface ImportMeta {
  readonly env: ImportMetaEnv;
}

CSS & Préprocesseurs

CSS Modules & PostCSS
// CSS classique — importé = injecté globalement
import './global.css';

// CSS Modules — scoped automatiquement
import styles from './Button.module.css';
return <button className={styles.btn}>...</button>;
// → class="Button_btn_a1b2c3" (unique)

/* Button.module.css */
.btn { background: blue; }
.btn:hover { background: darkblue; }

// PostCSS — automatique si postcss.config.js existe
// npm install -D autoprefixer postcss
// postcss.config.js :
export default {
  plugins: {
    autoprefixer: {},    // préfixes vendeurs automatiques
    'postcss-preset-env': { stage: 3 }
  }
}

// CSS variables dans vite.config.ts
css: {
  preprocessorOptions: {
    scss: {
      additionalData: `@import "./src/styles/variables.scss";`
    }
  }
}
SCSS, Less, Stylus & Tailwind
// SCSS — npm install -D sass
import './styles.scss';
import styles from './Component.module.scss';

// Less — npm install -D less
import './styles.less';

// Stylus — npm install -D stylus
import './styles.styl';

// Tailwind CSS — setup complet
# npm install -D tailwindcss postcss autoprefixer
# npx tailwindcss init -p

// tailwind.config.js
export default {
  content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
  theme: { extend: {} },
  plugins: [],
}

/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

// main.tsx
import './index.css';

// UnoCSS (alternative plus rapide)
# npm install -D unocss
// vite.config.ts → plugins: [UnoCSS()]

TypeScript

TypeScript dans Vite
// Vite supporte TS nativement SANS config spéciale
// Il transpile avec esbuild (pas tsc)
// → esbuild enlève les types, ne vérifie PAS

// tsconfig.json recommandé
{
  "compilerOptions": {
    "target": "ES2020",
    "lib": ["ES2020", "DOM"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "jsx": "react-jsx",
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/components/*"]
    }
  }
}

// Vérification de types (CI/CD)
# package.json
{
  "scripts": {
    "type-check": "tsc --noEmit",
    "build": "tsc --noEmit && vite build"
  }
}
Alias de chemins
// Sans alias — chemins relatifs imbriqués
import { Button } from '../../../components/ui/Button';

// Avec alias — propre et portable
import { Button } from '@/components/ui/Button';

// vite.config.ts
import { resolve } from 'path';

resolve: {
  alias: {
    '@': resolve(__dirname, 'src'),
    '@components': resolve(__dirname, 'src/components'),
    '@hooks':      resolve(__dirname, 'src/hooks'),
    '@utils':      resolve(__dirname, 'src/utils'),
    '@assets':     resolve(__dirname, 'src/assets'),
    '@types':      resolve(__dirname, 'src/types'),
  }
}

// tsconfig.json — même mapping pour l'IDE
"paths": {
  "@/*":           ["src/*"],
  "@components/*": ["src/components/*"],
  "@hooks/*":      ["src/hooks/*"],
  "@utils/*":      ["src/utils/*"]
}

// Plugin vite-tsconfig-paths (synchronise automatiquement)
# npm install -D vite-tsconfig-paths
import tsconfigPaths from 'vite-tsconfig-paths';
plugins: [react(), tsconfigPaths()]

Build de production

src/
Rollup
Tree-shaking
Minification
dist/
Options de build
build: {
  outDir:    'dist',       // dossier de sortie
  emptyOutDir: true,       // vider avant chaque build
  sourcemap: true,         // pour le debugging prod
  minify:   'terser',     // 'esbuild' (rapide) ou 'terser'
  target:   'es2015',     // navigateurs cibles

  // Taille des chunks
  chunkSizeWarningLimit: 500,  // warning si > 500kb

  // Assets
  assetsInlineLimit: 4096,  // inline si < 4kb
  assetsDir: 'assets',

  // CSS
  cssMinify: true,
  cssCodeSplit: true,  // 1 fichier CSS par chunk

  // Compatibilité navigateurs anciens
  // npm install -D @vitejs/plugin-legacy
}

# Analyser la taille du bundle
# npm install -D rollup-plugin-visualizer
plugins: [
  visualizer({ open: true, gzipSize: true })
]
Contenu du dossier dist/
dist/
├── index.html                 ← HTML avec tags <script> injectés
├── assets/
│   ├── index-B1c2d3e4.js     ← bundle principal (hash = cache)
│   ├── vendor-A5f6g7h8.js    ← dépendances (react, etc.)
│   ├── admin-C9d0e1f2.js     ← chunk lazy-loaded
│   ├── index-D3e4f5g6.css    ← styles
│   ├── logo-E7f8g9h0.png     ← image avec hash
│   └── font-F1g2h3i4.woff2
└── favicon.ico               ← depuis public/

# Tester le build en local
npm run build
npm run preview   → http://localhost:4173

# Déploiement
# dist/ est statique → déployable partout :
# Vercel, Netlify, GitHub Pages, Nginx, Apache

# Base URL pour sous-dossiers
# vite.config.ts
base: '/mon-app/'  ← si déployé sur example.com/mon-app/

Code splitting

Dynamic imports
// Import dynamique → chunk séparé automatiquement
const module = await import('./heavy-lib');

// React.lazy + Suspense
import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings  = lazy(() => import('./pages/Settings'));

function App() {
  return (
    <Suspense fallback={<Spinner />}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
      </Routes>
    </Suspense>
  );
}

// Nommer les chunks
const Dashboard = lazy(() =>
  import(
    /* webpackChunkName: "dashboard" */
    './pages/Dashboard'
  )
);

// Précharger un module
<link rel="modulepreload" href="/assets/dashboard-xxx.js">
// Ou via Vite : import(/* @vite-ignore */ './dashboard')
Contrôler les chunks (manualChunks)
build: {
  rollupOptions: {
    output: {
      // Séparer les dépendances en chunks nommés
      manualChunks: {
        // Vendor — bibliothèques rarement changées
        vendor: ['react', 'react-dom'],
        router: ['react-router-dom'],
        ui:     ['@mui/material', '@mui/icons-material'],
        charts: ['recharts'],
      },

      // Ou via une fonction
      manualChunks(id) {
        if (id.includes('node_modules')) {
          return 'vendor';
        }
        if (id.includes('src/features/admin')) {
          return 'admin';
        }
      }
    }
  }
}

// Résultat :
// vendor-xxx.js    — react + react-dom (mise en cache longue)
// router-xxx.js   — react-router-dom
// admin-xxx.js    — features/admin (lazy)
// index-xxx.js    — code applicatif

Optimisations

Pré-bundling & cache
optimizeDeps: {
  // Forcer l'inclusion dans le pré-bundle
  include: [
    'react', 'react-dom',
    'lodash-es',         // si chargement lent
  ],

  // Exclure du pré-bundle (servir tel quel)
  exclude: ['my-local-package'],

  // Options esbuild pour le pré-bundle
  esbuildOptions: {
    target: 'es2020'
  }
}

// Cache des dépendances
// Stocké dans node_modules/.vite/deps/
// Recalculé si package-lock.json change

# Forcer la recréation du cache
npx vite --force

# Ou supprimer manuellement
rm -rf node_modules/.vite
Terser & esbuild minification
build: {
  minify: 'esbuild',   // très rapide (défaut)
  minify: 'terser',    // plus agressif, plus lent
  minify: false,        // désactiver

  terserOptions: {       // si minify: 'terser'
    compress: {
      drop_console: true,     // supprimer console.log
      drop_debugger: true,
      pure_funcs: ['console.log', 'console.info'],
    },
    mangle: {
      safari10: true
    }
  }
}

// Compatibilité navigateurs anciens
# npm install -D @vitejs/plugin-legacy
import legacy from '@vitejs/plugin-legacy';

plugins: [
  legacy({
    targets: ['defaults', 'not IE 11'],
    additionalLegacyPolyfills: ['regenerator-runtime/runtime']
  })
]
// Génère un bundle moderne + un fallback pour anciens navigateurs

Plugins

@vitejs/plugin-react-swc
Support React avec SWC — Fast Refresh, JSX, décorateurs
@vitejs/plugin-vue
Support Vue 3 — SFC, template compiler
vite-plugin-svgr
Importer des SVG comme composants React
vite-tsconfig-paths
Lire les paths de tsconfig automatiquement
@vitejs/plugin-legacy
Support des navigateurs anciens (IE11, polyfills)
rollup-plugin-visualizer
Analyser la taille du bundle avec une carte interactive
vite-plugin-pwa
Progressive Web App — service worker, manifest
unplugin-auto-import
Auto-import des APIs (React, Vue hooks…) sans import
Écrire un plugin simple
// Un plugin Vite = un objet avec des hooks Rollup/Vite

function monPlugin() {
  return {
    name: 'mon-plugin',  // obligatoire

    // Transformer un fichier
    transform(code, id) {
      if (id.endsWith('.txt')) {
        return {
          code: `export default ${JSON.stringify(code)}`,
          map: null
        };
      }
    },

    // Résoudre un module virtuel
    resolveId(id) {
      if (id === 'virtual:config') return id;
    },
    load(id) {
      if (id === 'virtual:config') {
        return `export const version = "1.0.0"`;
      }
    },

    // Hooks de build
    buildStart()  { console.log('Build démarré'); },
    buildEnd()    { console.log('Build terminé'); },

    // Serveur de dev
    configureServer(server) {
      server.middlewares.use('/mon-endpoint', (req, res) => {
        res.end('Réponse custom');
      });
    }
  };
}

Proxy & CORS

Configurer un proxy API
server: {
  proxy: {
    // Simple — tout /api → backend
    '/api': 'http://localhost:8080',

    // Avancé — avec options
    '/api': {
      target:      'http://localhost:8080',
      changeOrigin: true,           // modifier l'en-tête Host
      rewrite:     (path) => path.replace(/^\/api/, ''),
      // /api/users → http://localhost:8080/users
    },

    // WebSocket
    '/ws': {
      target: 'ws://localhost:8080',
      ws:     true,
    },

    // HTTPS auto-signé
    '/api': {
      target: 'https://api.example.com',
      secure: false,  // accepter certificats auto-signés
    },

    // Plusieurs endpoints
    '/auth':    'http://auth-service:3001',
    '/users':   'http://user-service:3002',
    '/products': 'http://product-service:3003',
  }
}
Configuration selon l'environnement
// vite.config.ts — config conditionnelle
import { defineConfig, loadEnv } from 'vite';

export default defineConfig(({ command, mode }) => {
  // Charger les variables .env
  const env = loadEnv(mode, process.cwd(), '');

  return {
    plugins: [react()],

    define: {
      // Injecter des variables au build time
      __APP_VERSION__: JSON.stringify(process.env.npm_package_version),
      __BUILD_DATE__:  JSON.stringify(new Date().toISOString()),
    },

    server: {
      proxy: {
        '/api': env.VITE_API_URL || 'http://localhost:8080',
      }
    },

    build: {
      sourcemap: mode !== 'production',
    },

    // Dev uniquement
    ...(command === 'serve' ? {
      // config dev spécifique
    } : {
      // config build spécifique
    }),
  };
});

Multi-pages & mode bibliothèque

Application multi-pages (MPA)
// Plusieurs index.html = plusieurs points d'entrée
mon-app/
├── index.html          ← page principale
├── admin/index.html    ← page admin
├── login/index.html    ← page login
└── vite.config.ts

// vite.config.ts
import { resolve } from 'path';

build: {
  rollupOptions: {
    input: {
      main:  resolve(__dirname, 'index.html'),
      admin: resolve(__dirname, 'admin/index.html'),
      login: resolve(__dirname, 'login/index.html'),
    }
  }
}

// Build → dist/ contient les 3 pages
// Utile pour les sites multi-pages traditionnels
// + les apps qui mixent SPA et pages statiques
Mode bibliothèque (Library mode)
// Construire une bibliothèque NPM avec Vite

// vite.config.ts
import { resolve } from 'path';
import dts from 'vite-plugin-dts';  // génère .d.ts

export default defineConfig({
  plugins: [react(), dts()],
  build: {
    lib: {
      entry:   resolve(__dirname, 'src/index.ts'),
      name:    'MaBibliothèque',
      formats: ['es', 'cjs', 'umd'],
      fileName: (format) => `ma-lib.${format}.js`,
    },
    rollupOptions: {
      // Ne pas bundler les peer dependencies
      external: ['react', 'react-dom'],
      output: {
        globals: { react: 'React', 'react-dom': 'ReactDOM' }
      }
    }
  }
});

// Résultat :
// dist/ma-lib.es.js  ← ESM
// dist/ma-lib.cjs.js ← CommonJS
// dist/ma-lib.umd.js ← UMD (navigateur)
// dist/index.d.ts   ← Types TypeScript

Vitest — tests unitaires

Configurer et écrire des tests
# npm install -D vitest @vitest/ui jsdom
# Pour React : npm install -D @testing-library/react

// vite.config.ts
test: {
  globals:     true,      // describe, it, expect sans import
  environment: 'jsdom',  // DOM simulé
  setupFiles:  ['./src/setupTests.ts'],
  coverage: {
    provider:   'v8',
    reporter:   ['text', 'html'],
    exclude:    ['node_modules/', 'src/setupTests.ts'],
  }
}

// Écrire un test
// utils/sum.test.ts
import { describe, it, expect } from 'vitest';
import { sum } from './sum';

describe('sum', () => {
  it('additionne deux nombres', () => {
    expect(sum(1, 2)).toBe(3);
  });
  it('gère les négatifs', () => {
    expect(sum(-1, 1)).toBe(0);
  });
});
Tester des composants React
// Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { vi } from 'vitest';
import Button from './Button';

describe('Button', () => {
  it('affiche le label', () => {
    render(<Button label="Valider" />);
    expect(screen.getByText('Valider')).toBeInTheDocument();
  });

  it('appelle onClick au clic', () => {
    const onClick = vi.fn();
    render(<Button label="OK" onClick={onClick} />);
    fireEvent.click(screen.getByText('OK'));
    expect(onClick).toHaveBeenCalledTimes(1);
  });
});

// Commandes Vitest
# package.json
{
  "scripts": {
    "test":       "vitest",
    "test:ui":    "vitest --ui",    ← interface web
    "test:run":   "vitest run",     ← exécuter une fois
    "coverage":   "vitest run --coverage"
  }
}

Cheat sheet Vite

Commandes

npm create vite@latestCréer un projet
npm run devDev server (port 5173)
npm run buildBuild production → dist/
npm run previewTester le build local
npx vite --forceRecréer le cache deps

Assets & imports

import url from './img.png'URL hachée
?rawContenu en string
?urlToujours une URL
?workerWeb Worker
public/Copié sans transformation

Env vars

VITE_XXXExposé au client
import.meta.env.VITE_XXXLire la variable
import.meta.env.MODEdevelopment / production
import.meta.env.DEVtrue en dev
.env.localJamais commité

Optimisations

react-swc templateSWC > Babel
lazy() + SuspenseCode splitting routes
manualChunksSéparer vendor/app
drop_console: trueTerser en prod
visualizer pluginAnalyser le bundle