Tous les articles

·4 min de lecture

Optimiser le temps de build Angular avec esbuild : stratégies concrètes pour 30% de gain

esbuild est 10× plus rapide que Webpack, mais mal configuré, il peut devenir un goulot. Découvrez comment réduire vos temps de build de 30 à 50% en production et développement.

Angular 17+ utilise esbuild par défaut, une décision majeure qui a transformé la vitesse de compilation. Contrairement à Webpack, esbuild traite le code en Go, ce qui lui permet de paralléliser naturellement et d'éviter les surcharges de mémoire. Cependant, passer à esbuild ne signifie pas que votre build est automatiquement optimal. La configuration par défaut d'Angular fonctionne bien pour les petits projets, mais dès que vous dépassez 500 composants ou que vous intégrez des micro-frontends, vous rencontrez des ralentissements. Le secret n'est pas de « forcer » esbuild, mais de comprendre où il dépense ses cycles.

Identifier les vrais goulots : au-delà du feeling

Trop de développeurs optimisent au hasard. Avant de toucher à quoi que ce soit, activez la mesure précise. Angular CLI vous donne déjà des informations via ng build --configuration production --stats-json . Cette commande génère un fichier stats.json que vous pouvez analyser avec webpack-bundle-analyzer ou source-map-explorer . Oui, c'est contre-intuitif d'utiliser des outils Webpack pour analyser un build esbuild, mais ces outils lisent le bundle final, pas le système de build. Vous découvrirez rapidement vos vraies sources de bloat : dépendances inutiles, polyfills non-élagués, ou du code mort qui n'a pas été tree-shaken. Une équipe que j'ai conseillée a trouvé 200 KB de dépendances inutilisées en 10 minutes de lecture du bundle. Sans cette mesure, ils auraient perdu des heures à optimiser le mauvais truc.

Une autre métrique cruciale : le temps de build par étape. Lancez ng build --configuration production --verbose pour voir le détail. esbuild lui-même est rapide, mais Angular effectue plusieurs étapes avant et après : AOT compilation, tree-shaking, minification, génération de source maps. Chacune peut prendre du temps selon votre configuration. Si vous voyez que la compilation AOT prend 40 secondes sur 60, l'optimisation du bundling ne vous aide que marginalement. C'est un problème d'analyse, pas de bundler.

Configurer esbuild pour le parallélisme et la mémoire

Par défaut, Angular ne configure pas explicitement esbuild pour maximiser le parallélisme. Vous pouvez le faire via angular.json . La clé est esbuildOptions dans la configuration de build. Voici une approche concrète :

"build": {
  "options": {
    "esbuildOptions": {
      "define": {
        "ngDevMode": false,
        "ngI18nClosureMode": false
      },
      "logLevel": "warning",
      "splitting": true,
      "minify": true,
      "sourcemap": false,
      "target": ["ES2022"]
    }
  },
  "configurations": {
    "production": {
      "budgets": [{"type": "bundle", "maximumWarning": "500kb", "maximumError": "1mb"}],
      "sourceMap": false,
      "outputHashing": "all"
    }
  }
}

La directive splitting est essentielle : elle force esbuild à créer plusieurs chunks au lieu d'un seul bundle, ce qui améliore le parallélisme et permet au navigateur de charger le code plus efficacement. logLevel: warning réduit le bruit des logs, ce qui accélère aussi la sortie. Supprimer sourcemap en production est évident, mais beaucoup le font aussi en développement, perdant une source de débogage précieuse. À la place, utilisez des source maps inline ou déportées dans un CDN séparé.

Lazy loading et code splitting : l'art de ne charger que ce qu'on utilise

esbuild excelle quand vous lui donnez des chunks distincts à compiler. Le problème : beaucoup de projets Angular chargent tout au démarrage. Vérifiez votre routing : chaque route doit être lazy-loaded sauf le module racine. C'est basique, mais j'ai vu des apps de 50 composants qui chargeaient 95% du code au démarrage. Voici le pattern correct :

const routes: Routes = [
  { path: '', component: HomeComponent },
  {
    path: 'admin',
    loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
    canActivate: [AuthGuard]
  },
  {
    path: 'dashboard',
    loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule)
  }
];

Au-delà du routing, Angular 14+ supporte les component lazy-loaded sans module. Cela réduit la verbosité et améliore l'isolation. Chaque chunk lazy-loaded est compilé indépendamment par esbuild, ce qui signifie que si vous modifiez le module admin, seul ce chunk est rebuildé en développement. Sur une app de 100 KB de code métier réparti en 8 modules, cela passe de 8 secondes de rebuild à 2 secondes.

Dépendances externes et `node_modules` : le piège silencieux

esbuild bundlise tout, y compris node_modules , ce qui est correct pour la production mais catastrophique en développement. Chaque npm install ou upgrade de dépendance force un rebuild complet du bundle parce que esbuild doit re-parser tous les fichiers. La solution : configurez externalDependencies pour certaines librairies. Cependant, attention : cela ne fonctionne que si vous servez ces dépendances via un CDN ou un loader séparé. Pour la plupart des apps, c'est impraticable. À la place, limitez vos dépendances. Une équipe a réduit de 40 dépendances à 15 en utilisant les APIs natives (Fetch au lieu d'Axios, RxJS au lieu de lodash, etc.). Le temps de build a chuté de 45 secondes à 18 secondes, et le bundle final a perdu 120 KB.

Inspectez votre package.json avec npm ls pour détecter les dépendances en double ou les versions conflictuelles. esbuild doit les résoudre à chaque build. Un simple npm dedupe peut gagner quelques secondes.

Pièges courants : ce qui ralentit vraiment

Beaucoup de développeurs activent sourceMap: true partout, même en production. Les source maps peuvent doubler le temps de build et triplier la taille du bundle. Utilisez-les en développement seulement, et encore, préférez les eval-source-maps qui sont plus rapides. Un autre piège : trop de polyfills . Angular inclut des polyfills pour IE11 par défaut dans les vieilles versions. Si vous supportez ES2020+, supprimez-les. Enfin, les lazy-loaded modules qui ne sont jamais vraiment lazy parce qu'ils sont importés dans le module racine. Vérifiez vos imports : si vous faites import { AdminComponent } from './admin/admin.component' au lieu d'un import dynamique, vous cassez le lazy-loading.

Une erreur classique : configurer optimization manuellement en pensant améliorer esbuild. esbuild gère déjà la minification, l'élagage et la déduplication. Ajouter des options Webpack supplémentaires ne fait que créer de la confusion.

Conclusion : mesurer, cibler, valider

Optimiser le temps de build Angular avec esbuild, c'est d'abord comprendre où le temps s'écoule. Activez --stats-json , mesurez chaque étape, puis appliquez les changements dans cet ordre : 1) Lazy-load agressivement, 2) Élaguez les dépendances, 3) Configurez esbuild pour le parallélisme, 4) Tuning fin des source maps et polyfills. Ne visez pas 0 secondes, visez 20-30 secondes en production et 5 secondes en développement. C'est réaliste et impactant. Mesurez avant et après chaque changement avec time ng build pour éviter l'optimisation prématurée. L'effort vaut la peine : une équipe de 10 devs qui gagne 30 secondes par build, c'est 50 heures économisées par mois.