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
Pourquoi un bundler ?
Section intitulée « Pourquoi un bundler ? »À 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 :
- Transpilation : TS, JSX, Sass, etc. → JS standard.
- Concaténation : moins de requêtes HTTP (vrai en HTTP/1, moins critique en HTTP/2/3).
- Optimisation : minification, tree-shaking, splitting.
- Polyfills : ajouter ce qui manque pour les anciens navigateurs.
Le paysage des outils
Section intitulée « Le paysage des outils »| Outil | Année | Vitesse | Usage typique |
|---|---|---|---|
| Webpack | 2014 | 🐢 lent | Legacy ; encore présent dans des CRA et Next ≤ 15 |
| Rollup | 2015 | 🐇 rapide | Surtout pour les libs (output ESM propre) |
| Parcel | 2017 | 🐇 rapide | Zero-config, niche |
| esbuild | 2020 | 🚀 ultra rapide (Go) | Sous-jacent à beaucoup d’outils |
| SWC | 2020 | 🚀 ultra rapide (Rust) | Remplace Babel dans Next.js |
| Vite | 2020 | 🚀 (Rolldown depuis v8) | Standard 2026+ pour la majorité des projets non-Next |
| Rolldown | 2025 | 🚀 (Rust) | Bundler unifié de Vite — remplace esbuild et Rollup en interne |
| Turbopack | 2022 | 🚀 (Rust) | Bundler par défaut de Next.js 16 |
| Rspack | 2023 | 🚀 (Rust) | Drop-in replacement Webpack — meilleurs cold starts |
| Bun bundler | 2024 | 🚀 ultra rapide | Inclus dans Bun |
Comparaison conceptuelle
Section intitulée « Comparaison conceptuelle »- 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.
Vite — l’expérience moderne par défaut
Section intitulée « Vite — l’expérience moderne par défaut »npm create vite@latest mon-app -- --template react-tscd mon-app && npm install && npm run devAvec 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).
Pourquoi Vite est rapide
Section intitulée « Pourquoi Vite est rapide »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.
Configuration minimale
Section intitulée « Configuration minimale »import { defineConfig } from 'vite';import react from '@vitejs/plugin-react';
export default defineConfig({ plugins: [react()], server: { port: 5173, }, build: { target: 'esnext', sourcemap: true, },});Variables d’environnement
Section intitulée « Variables d’environnement »VITE_API_URL=https://api.example.comSECRET_KEY=xxx # PAS exposé (pas de prefix VITE_)console.log(import.meta.env.VITE_API_URL); // OKconsole.log(import.meta.env.SECRET_KEY); // undefined côté clientRè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).
Tree-shaking — éliminer le code mort
Section intitulée « Tree-shaking — éliminer le code mort »Le tree-shaking supprime les exports non utilisés du bundle final.
export function used() { /* ... */ }export function unused() { /* gros code jamais appelé */ }
// main.jsimport { 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.
Conditions pour que ça marche
Section intitulée « Conditions pour que ça marche »- Modules ESM (pas CommonJS).
- Imports nommés, pas le module entier (
import * as utils). - Pas d’effets de bord déclarés ou détectés.
// ❌ Bloque le tree-shaking d'utilsimport * as utils from './utils';
// ✅import { used } from './utils';Side effects et package.json
Section intitulée « Side effects et package.json »{ "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 debounceimport debounce from 'lodash/debounce';
// ✅ ou utilise lodash-es (ESM tree-shakable)import { debounce } from 'lodash-es';Code-splitting — découper le bundle
Section intitulée « Code-splitting — découper le bundle »Plutôt qu’un seul bundle.js de 2 Mo, on découpe en chunks chargés à la demande.
Code-splitting automatique
Section intitulée « Code-splitting automatique »Les méta-frameworks (Next.js, Nuxt, SvelteKit) splittent automatiquement par route. Chaque page a son propre chunk + un chunk commun (vendor).
Code-splitting manuel — dynamic import
Section intitulée « Code-splitting manuel — dynamic import »// Lazy : charger uniquement quand on en a besoinconst 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.
// Vanillaconst heavy = await import('./heavy.js');heavy.doStuff();Inspecter ton bundle
Section intitulée « Inspecter ton bundle »Quand le bundle gonfle, on cherche qui prend de la place.
Vite — visualiseur de bundle
Section intitulée « Vite — visualiseur de bundle »npm install -D rollup-plugin-visualizerimport { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({ plugins: [react(), visualizer({ open: true })],});npm run build# ouvre stats.html — treemap qui montre la taille de chaque moduleOutil universel — bundle-analyzer
Section intitulée « Outil universel — bundle-analyzer »Pour Next.js : @next/bundle-analyzer.
Pour Webpack : webpack-bundle-analyzer.
Coupables fréquents
Section intitulée « Coupables fréquents »| Coupable | Solution |
|---|---|
lodash complet | Importer par fonction : lodash/debounce ou lodash-es |
moment.js | Remplacer par date-fns ou dayjs (10× plus léger) |
axios | fetch natif suffit dans 95 % des cas |
react-icons complet | Importer une icône à la fois : react-icons/fa |
| Images dans le bundle | Servir depuis /public ou un CDN |
| Police complète Google Fonts | Self-host avec subset (woff2) |
Optimisations bonus
Section intitulée « Optimisations bonus »target: esnext en build
Section intitulée « target: esnext en build »Si tu cibles uniquement les navigateurs récents (last 2 versions) :
build: { target: 'es2022',}→ pas de polyfills inutiles, bundle plus petit.
Compression Brotli/Gzip
Section intitulée « Compression Brotli/Gzip »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;Subresource Integrity (SRI)
Section intitulée « Subresource Integrity (SRI) »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é.
Auto-évaluation
Section intitulée « Auto-évaluation »Pour aller plus loin
Section intitulée « Pour aller plus loin »- Vite Docs — vitejs.dev
- Tobias Koppers — Webpack Inception talk — pour comprendre l’histoire
- web.dev — Reduce JavaScript payloads — web.dev/articles/reduce-javascript-payloads-with-tree-shaking
- Bundle Phobia — bundlephobia.com — taille d’un package npm avant de l’installer
Suite : 7.6 — Design systems & UI kits — Tailwind, shadcn/ui, Radix, Storybook.