Esc
 Naviguer  Ouvrir Esc Fermer
Aller au contenu

7.5 — Outils de build

🎯 Objectif : comprendre ce qui se passe quand tu fais npm run build, et savoir débugger un bundle qui devient gros sans raison.

À l'issue de cet axe, tu sauras :

  • Comprendre le rôle d'un bundler (transformer + concaténer + optimiser)
  • Distinguer Vite, esbuild, SWC, Webpack, Rollup
  • Expliquer tree-shaking, code-splitting, dynamic imports
  • Inspecter un bundle et identifier les gros morceaux
  • Configurer un projet Vite pour un cas non standard

Débutant 10 min prérequis : axe 6 lu

À l’origine, le navigateur ne supportait pas import/export côté client. Aujourd’hui c’est résolu (ESM natif), mais on bundle toujours pour 4 raisons :

  1. Transpilation : TS, JSX, Sass, etc. → JS standard.
  2. Concaténation : moins de requêtes HTTP (vrai en HTTP/1, moins critique en HTTP/2/3).
  3. Optimisation : minification, tree-shaking, splitting.
  4. Polyfills : ajouter ce qui manque pour les anciens navigateurs.
OutilAnnéeVitesseUsage typique
Webpack2014🐢 lentLegacy ; encore présent dans des CRA et Next ≤ 15
Rollup2015🐇 rapideSurtout pour les libs (output ESM propre)
Parcel2017🐇 rapideZero-config, niche
esbuild2020🚀 ultra rapide (Go)Sous-jacent à beaucoup d’outils
SWC2020🚀 ultra rapide (Rust)Remplace Babel dans Next.js
Vite2020🚀 (Rolldown depuis v8)Standard 2026+ pour la majorité des projets non-Next
Rolldown2025🚀 (Rust)Bundler unifié de Vite — remplace esbuild et Rollup en interne
Turbopack2022🚀 (Rust)Bundler par défaut de Next.js 16
Rspack2023🚀 (Rust)Drop-in replacement Webpack — meilleurs cold starts
Bun bundler2024🚀 ultra rapideInclus dans Bun
  • esbuild et SWC sont des transformers ultra-rapides (parsing + transpilation), souvent utilisés en interne par d’autres outils.
  • Webpack et Rollup sont des bundlers complets (graphe de dépendances + bundling) — fondations historiques.
  • Rolldown (par l’équipe Vite) est le successeur unifié de esbuild + Rollup, en Rust.
  • Vite ≥ 8 utilise Rolldown en dev et en build — fini la dichotomie esbuild/Rollup.
Fenêtre de terminal
npm create vite@latest mon-app -- --template react-ts
cd mon-app && npm install && npm run dev

Avec Vite 8 (2026), dev et build utilisent Rolldown (Rust). Tu obtiens :

  • Cold start instantané même sur gros projets.
  • Build de prod 3 à 5× plus rapide que Vite 7.
  • HMR sub-100 ms (dans la plage de Turbopack).

En dev :

  • ESM natif côté navigateur — pas de bundle global.
  • Vite compile à la volée chaque fichier demandé par le navigateur, avec Rolldown (Vite 8+) ou esbuild (≤ 7).
  • Hot Module Replacement (HMR) instantané.

En build :

  • Rolldown (Vite 8+, Rust) produit un bundle minifié unifié.
  • Vite 7 et antérieurs utilisaient Rollup en build.
vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
port: 5173,
},
build: {
target: 'esnext',
sourcemap: true,
},
});
.env
VITE_API_URL=https://api.example.com
SECRET_KEY=xxx # PAS exposé (pas de prefix VITE_)
console.log(import.meta.env.VITE_API_URL); // OK
console.log(import.meta.env.SECRET_KEY); // undefined côté client

Règle : seules les variables avec le préfixe VITE_ sont exposées au bundle client. Les autres restent côté serveur (utile pour Vite SSR ou en build time).

Le tree-shaking supprime les exports non utilisés du bundle final.

utils.js
export function used() { /* ... */ }
export function unused() { /* gros code jamais appelé */ }
// main.js
import { used } from './utils.js';
used();

Avec un import nommé, le bundler détecte que unused n’est jamais utilisée → elle disparaît du bundle.

  1. Modules ESM (pas CommonJS).
  2. Imports nommés, pas le module entier (import * as utils).
  3. Pas d’effets de bord déclarés ou détectés.
// ❌ Bloque le tree-shaking d'utils
import * as utils from './utils';
// ✅
import { used } from './utils';
{
"name": "ma-lib",
"sideEffects": false // ← dit aux bundlers : "tu peux tree-shaker"
}

Si une lib n’a pas sideEffects: false, le bundler est conservateur et garde des choses inutiles. C’est pourquoi certaines libs (lodash classique) n’étaient pas tree-shakable. Solutions :

// ❌ tout lodash importé
import _ from 'lodash';
// ✅ uniquement debounce
import debounce from 'lodash/debounce';
// ✅ ou utilise lodash-es (ESM tree-shakable)
import { debounce } from 'lodash-es';

Plutôt qu’un seul bundle.js de 2 Mo, on découpe en chunks chargés à la demande.

Les méta-frameworks (Next.js, Nuxt, SvelteKit) splittent automatiquement par route. Chaque page a son propre chunk + un chunk commun (vendor).

// Lazy : charger uniquement quand on en a besoin
const AdminPanel = lazy(() => import('./AdminPanel'));
function App() {
const isAdmin = useUser().role === 'admin';
return (
<Suspense fallback={<Spinner />}>
{isAdmin && <AdminPanel />}
</Suspense>
);
}

L’AdminPanel et toutes ses dépendances sont dans un chunk séparé, téléchargé uniquement par les admins.

// Vanilla
const heavy = await import('./heavy.js');
heavy.doStuff();

Quand le bundle gonfle, on cherche qui prend de la place.

Fenêtre de terminal
npm install -D rollup-plugin-visualizer
vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [react(), visualizer({ open: true })],
});
Fenêtre de terminal
npm run build
# ouvre stats.html — treemap qui montre la taille de chaque module

Pour Next.js : @next/bundle-analyzer.

Pour Webpack : webpack-bundle-analyzer.

CoupableSolution
lodash completImporter par fonction : lodash/debounce ou lodash-es
moment.jsRemplacer par date-fns ou dayjs (10× plus léger)
axiosfetch natif suffit dans 95 % des cas
react-icons completImporter une icône à la fois : react-icons/fa
Images dans le bundleServir depuis /public ou un CDN
Police complète Google FontsSelf-host avec subset (woff2)

Si tu cibles uniquement les navigateurs récents (last 2 versions) :

vite.config.ts
build: {
target: 'es2022',
}

→ pas de polyfills inutiles, bundle plus petit.

Les CDN modernes (Vercel, Netlify, Cloudflare) servent en Brotli par défaut. En self-host avec Nginx :

gzip on;
gzip_types application/javascript text/css;
brotli on;
brotli_types application/javascript text/css;

Pour les CDN externes, ajoute un hash :

<script
src="https://cdn.example.com/lib.js"
integrity="sha384-..."
crossorigin="anonymous">
</script>

Le navigateur refuse d’exécuter si le contenu a été altéré.

Tu importes lodash dans ton app React et le bundle gonfle de 70 Ko. Solution ?
En dev, Vite est instantané même sur un gros projet. Pourquoi pas Webpack ?
Tu veux qu'un panel admin (10 % des utilisateurs) ne soit téléchargé QUE pour les admins. Comment ?

Suite : 7.6 — Design systems & UI kits — Tailwind, shadcn/ui, Radix, Storybook.