Avez-vous une question? (216) 97 656 803 hajjriadh@gmail.com
Angular

Compiler un projet Angular pour la publication (ng build)

Un cours complet pour maîtriser Compiler un projet Angular pour la publication (ng build).

0 chapitres 0 QCM Accès gratuit
Dans cet article
  1. Objectifs

    • Compiler une application avec ng build et savoir ce que contient le dossier dist/
    • Lire le rapport de compilation : fichiers initiaux, taille brute et taille transférée
    • Distinguer la configuration de production et celle de développement
    • Comprendre les noms de fichiers avec empreinte (main-7W7XMOG5.js) et leur effet sur le cache
    • Utiliser des fichiers d'environnement pour changer l'adresse d'une API selon la cible
    • Régler les budgets de taille et tester la version compilée sur sa machine
  1. Présentation

    • Pendant le développement, ng serve compile l'application en mémoire, surveille les fichiers et recharge le navigateur. Rien n'est écrit sur le disque, et ce serveur n'est pas fait pour être exposé sur Internet.
    • Pour publier, on lance ng build: Angular transforme le TypeScript, les templates et les styles en quelques fichiers statiques (HTML, JavaScript, CSS) optimisés. Ces fichiers suffisent : le serveur qui les héberge n'a besoin ni de Node.js ni d'Angular, un simple serveur web (Apache, Nginx, GitHub Pages…) fait l'affaire.
    • Cette leçon décrit la compilation ; les deux suivantes montrent où déposer le résultat : sur un hébergement web ou un serveur dédié, puis sur GitHub Pages.
  • Pour suivre ce chapitre, continuez dans le projet premiers-pas-angular. Les exemples utilisent trois pages reliées par le routeur : /, /cours et /cours/:id. Toutes les commandes ont été exécutées et testées avec Angular 22.2 (Angular CLI 22.2.6, Node.js 22).

ng serve compile en mémoire pour développer, ng build écrit les fichiers à publier dans dist/

  1. La commande ng build

    • Placez-vous à la racine du projet (le dossier qui contient angular.json) et lancez :
ng build
  • Angular affiche un rapport, puis l'emplacement du résultat :
✔ Building...
Initial chunk files | Names         |  Raw size | Estimated transfer size
main-7W7XMOG5.js    | main          | 215.64 kB |                59.17 kB
styles-5INURTSO.css | styles        |   0 bytes |                 0 bytes

                    | Initial total | 215.64 kB |                59.17 kB

Application bundle generation complete. [3.296 seconds]

Output location: /home/claude/ng/premiers-pas-angular/dist/premiers-pas-angular

Rapport de ng build : fichiers initiaux, taille brute et taille estimée après compression

  • Initial chunk files: les fichiers chargés dès l'ouverture de la page. Si vous chargez des routes à la demande (loadComponent), une seconde partie Lazy chunk files apparaît.
  • Raw size: la taille réelle des fichiers sur le disque.
  • Estimated transfer size: la taille estimée après compression par le serveur (gzip ou brotli). C'est elle qui compte pour l'utilisateur : ici, environ 59 kB à télécharger.
  • La compilation est confiée au builder @angular/build:application, déclaré dans angular.json; il s'appuie sur esbuild et ne prend que quelques secondes.
  1. Le contenu du dossier dist

    • Le résultat est écrit dans dist/<nom-du-projet>/. Seul le sous-dossier browser/ est à publier :
dist/
└── premiers-pas-angular/
    ├── 3rdpartylicenses.txt      ← licences des bibliothèques utilisées (ne pas publier)
    └── browser/                  ← LE DOSSIER À PUBLIER
        ├── index.html
        ├── main-7W7XMOG5.js
        ├── styles-5INURTSO.css
        └── favicon.ico           ← copié depuis le dossier public/
  • Le fichier index.html produit est très court : il déclare l'adresse de base (<base href="/">), charge la feuille de style et le script principal, et contient la balise <app-root> que l'application va remplir :
<!doctype html>
<html lang="en" data-beasties-container>
<head>
  <meta charset="utf-8">
  <title>PremiersPasAngular</title>
  <base href="/">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="stylesheet" href="styles-5INURTSO.css"></head>
<body>
  <app-root></app-root>
<script src="main-7W7XMOG5.js" type="module"></script></body>
</html>
  • Chaque compilation vide d'abord le dossier de sortie : ne rangez jamais de fichiers personnels dans dist/. Ce dossier est d'ailleurs ignoré par Git (.gitignore), car on peut toujours le recréer.
  • Les fichiers du dossier public/ (images, favicon, polices…) sont recopiés tels quels à la racine de browser/.
  1. Production ou développement

    • Dans angular.json, la cible build possède deux configurations, et defaultConfiguration indique laquelle est utilisée quand on n'en précise aucune :
"build": {
  "builder": "@angular/build:application",
  "options": {
    "browser": "src/main.ts",
    "tsConfig": "tsconfig.app.json",
    "assets": [{ "glob": "**/*", "input": "public" }],
    "styles": ["src/styles.css"]
  },
  "configurations": {
    "production": {
      "budgets": [ … ],
      "outputHashing": "all"
    },
    "development": {
      "optimization": false,
      "extractLicenses": false,
      "sourceMap": true
    }
  },
  "defaultConfiguration": "production"
}
  • ng build compile donc en production. Pour obtenir une version lisible, à déboguer, on demande l'autre configuration :
ng build --configuration development

Initial chunk files | Names         | Raw size
main.js             | main          |  1.36 MB
styles.css          | styles        | 95 bytes
Réglage production (par défaut) development
Optimisation (code minifié, code mort supprimé) Oui Non (optimization: false)
Noms de fichiers Avec empreinte : main-7W7XMOG5.js Fixes : main.js
Source maps (.map) Non Oui (débogage du TypeScript dans le navigateur)
Taille de main pour notre projet 215,64 kB (59 kB transférés) 1,36 MB
Budgets vérifiés Oui Non
Usage Publication Déboguer un problème qui n'apparaît qu'après compilation
  • Publiez toujours la version de production : six fois plus légère, elle ne livre pas votre code source aux visiteurs à travers les source maps.
  1. Les noms de fichiers avec empreinte et le cache

    • En production, chaque nom de fichier contient une empreinte (hash) calculée à partir de son contenu : main-7W7XMOG5.js. Modifiez une ligne de code, recompilez : le fichier s'appelle autrement, et index.html pointe vers le nouveau nom.
    • C'est le réglage "outputHashing": "all". Il permet au serveur de demander au navigateur de garder ces fichiers en cache très longtemps: un fichier modifié changera de nom, il ne sera jamais confondu avec l'ancien.
    • Seul index.html garde toujours le même nom : c'est lui qui ne doit pas être mis en cache, sinon le navigateur continuerait à charger les anciens scripts. La leçon suivante montre comment le régler sur le serveur.

index.html jamais en cache, fichiers avec empreinte en cache pendant un an

  1. Les fichiers d'environnement

    • Une application appelle souvent une API dont l'adresse diffère entre la machine du développeur et le serveur de production. Angular crée deux fichiers d'environnement avec :
ng generate environments

CREATE src/environments/environment.ts (31 bytes)
CREATE src/environments/environment.development.ts (31 bytes)
UPDATE angular.json (2250 bytes)
  • Remplissez-les avec les mêmes propriétés:
// src/environments/environment.ts : utilisé par ng build (production)
export const environment = {
  production: true,
  apiUrl: 'https://api.mon-site.tn',
};

// src/environments/environment.development.ts : utilisé par ng serve et --configuration development
export const environment = {
  production: false,
  apiUrl: 'http://localhost:8000',
};
  • La commande a ajouté une règle de remplacement dans la configuration development de angular.json: pendant la compilation, le premier fichier est remplacé par le second.
"development": {
  "optimization": false,
  "extractLicenses": false,
  "sourceMap": true,
  "fileReplacements": [
    {
      "replace": "src/environments/environment.ts",
      "with": "src/environments/environment.development.ts"
    }
  ]
}
  • Dans le code, on importe toujours environment.ts, jamais la version .development:
import { Component } from '@angular/core';
import { RouterLink, RouterOutlet } from '@angular/router';
import { environment } from '../environments/environment';

@Component({
  imports: [RouterOutlet, RouterLink],
  selector: 'app-root',
  styleUrl: './app.css',
  templateUrl: './app.html',
})
export class App {
  protected readonly api = environment.apiUrl;
}
  • Vérification faite sur les fichiers compilés : le main-….js de production ne contient que https://api.mon-site.tn, et le main.js de développement que http://localhost:8000.
  • Ces fichiers sont livrés au navigateur : n'y mettez jamais de mot de passe, de clé secrète ou d'identifiants de base de données. Tout visiteur peut les lire.
  1. Les budgets

    • Les budgets de la configuration de production surveillent le poids de l'application : un avertissement au-delà de maximumWarning, une erreur qui arrête la compilation au-delà de maximumError.
"budgets": [
  {
    "type": "initial",
    "maximumWarning": "500kB",
    "maximumError": "1MB"
  },
  {
    "type": "anyComponentStyle",
    "maximumWarning": "4kB",
    "maximumError": "8kB"
  }
]
  • initial limite le total des fichiers chargés à l'ouverture ; anyComponentStyle limite la feuille de style de chaque composant.
  • Pour voir l'effet, abaissez maximumError à 100kB et recompilez :
Application bundle generation failed. [2.600 seconds]

✘ [ERROR] bundle initial exceeded maximum budget. Budget 100.00 kB was not met by 115.64 kB with a total of 215.64 kB.

Erreur : bundle initial exceeded maximum budget

  • Face à un dépassement, cherchez d'abord à alléger (charger des routes à la demande avec loadComponent, retirer une bibliothèque inutile) avant de relever la limite.
  1. Tester la version compilée sur sa machine

    • Un double-clic sur dist/premiers-pas-angular/browser/index.html affiche une page blanche. La console explique pourquoi : le navigateur refuse de charger un script type="module" depuis une adresse file://.
Access to script at 'file:///main-7W7XMOG5.js' from origin 'null' has been blocked by CORS policy:
Cross origin requests are only supported for protocol schemes: chrome, chrome-untrusted, data, http, https.

Page blanche et erreur CORS en ouvrant index.html par un double-clic

  • Il faut passer par un petit serveur web. Le paquet serve s'utilise sans installation, avec npx; l'option -s (single-page) renvoie index.html pour toutes les adresses inconnues, comme le fera le vrai serveur :
ng build
npx serve -s dist/premiers-pas-angular/browser -l 4300

 INFO  Accepting connections at http://localhost:4300
  • Ouvrez http://localhost:4300/cours/3: la page du cours « Signals » s'affiche directement, ce qui prouve que la version de production et le routeur fonctionnent.

La version compilée servie par npx serve : page du cours Signals

  1. Publier dans un sous-dossier : --base-href

    • La balise <base href="/"> de index.html indique à partir de quelle adresse le navigateur cherche les fichiers et le routeur construit ses liens. Si l'application doit vivre dans un sous-dossier (par exemple https://mon-site.tn/formation/), on le précise à la compilation :
ng build --base-href /formation/
<base href="/formation/">
  • N'oubliez pas les deux barres obliques. ng build --base-href formation est accepté sans erreur, mais produit <base href="formation">, qui casse les adresses dès que l'on quitte la page d'accueil.
  • La leçon suivante montre l'installation complète dans un sous-dossier, avec la configuration du serveur.

Aide-mémoire : compiler un projet Angular avec ng build

  1. Activité 1 : lire un rapport de compilation

    • Lancer ng build dans votre projet et recopier, pour chaque fichier initial, son nom, sa taille brute et sa taille transférée.
    • Ajouter une phrase dans le template d'un composant, recompiler, et comparer le nom du fichier main-….js avant et après.
  • Vérifier : le nom change, celui de styles-….css ne change pas si aucun style n'a été modifié.
  • Pourquoi ce changement de nom est-il une bonne chose pour les visiteurs déjà venus sur le site ?
  1. Activité 2 : production contre développement

    • Compiler successivement avec ng build puis ng build --configuration development, et remplir un tableau : nom des fichiers, taille de main, présence de fichiers .map.
    • Ouvrir les deux fichiers main dans l'éditeur et comparer leur lisibilité.
  • Vérifier : la version de développement est plusieurs fois plus lourde et contient des fichiers .map.
  • Laquelle publier, et pourquoi ne faut-il jamais publier l'autre ?
  1. Activité 3 : une API par environnement

    • Générer les fichiers d'environnement et y déclarer apiUrl: http://localhost:8000 en développement, https://api.mon-site.tn en production.
    • Afficher cette adresse en bas de la page d'accueil, puis comparer l'affichage sous ng serve et sous npx serve -s après ng build.
  • Vérifier : ng serve affiche l'adresse locale, la version compilée celle de production.
  • Un camarade propose d'y ranger aussi le mot de passe de la base de données : que lui répondez-vous ?
  1. Activité 4 : déclencher et corriger un dépassement de budget

    • Abaisser le budget initial à 100kB (erreur) et observer le message.
    • Rétablir la valeur d'origine, puis abaisser anyComponentStyle à 1kB et ajouter une grosse feuille de style dans un composant.
  • Vérifier : la compilation échoue avec un message qui nomme le budget dépassé.
  • Pourquoi l'équipe d'Angular a-t-elle choisi de bloquer la compilation plutôt que d'afficher un simple avertissement ?
  1. Activité 5 : préparer une publication dans un sous-dossier

    • Compiler avec --base-href /formation/ et ouvrir dist/premiers-pas-angular/browser/index.html dans l'éditeur.
    • Recommencer avec --base-href formation, sans les barres obliques.
  • Vérifier : la balise <base> vaut /formation/ puis formation.
  • Quelle valeur faut-il garder, et que se passera-t-il avec l'autre ?
  1. Erreurs fréquentes

Message ou symptôme Cause Solution
Page blanche après un double-clic sur index.html, erreur blocked by CORS policy Fichiers ouverts en file:// Passer par un serveur : npx serve -s dist/…/browser
bundle initial exceeded maximum budget Application trop lourde pour le budget Alléger (routes à la demande, bibliothèques inutiles), sinon ajuster le budget
L'application publiée appelle localhost Adresse écrite en dur, ou import de environment.development.ts Importer environment.ts et compiler en production
Fichiers .map et code lisible sur le serveur Compilation avec --configuration development Recompiler avec ng build
Un fichier rangé dans dist/ a disparu Chaque compilation vide le dossier de sortie Ranger les fichiers à publier dans public/
Le serveur affiche la liste des fichiers ou une page 403 Dossier dist/premiers-pas-angular publié au lieu de browser/ Publier le contenu de browser/
Liens cassés dans un sous-dossier, <base href="formation"> Barres obliques oubliées dans --base-href --base-href /formation/
  1. À retenir

    • ng build compile en production par défaut et écrit le résultat dans dist/<projet>/browser/: c'est le contenu de ce dossier que l'on publie.
    • Le résultat est fait de fichiers statiques : un simple serveur web suffit, sans Node.js.
    • Les noms avec empreinte (main-7W7XMOG5.js) changent à chaque modification : ces fichiers peuvent rester longtemps en cache, mais pas index.html.
    • ng generate environments crée deux fichiers ; on importe toujours environment.ts, et on n'y met aucun secret.
    • Les budgets arrêtent la compilation si l'application devient trop lourde.
    • Pour tester la version compilée : npx serve -s dist/<projet>/browser, jamais un double-clic.
    • --base-href /dossier/ prépare une publication dans un sous-dossier.
Le dossier dist/ est le seul livrable : ce qui n'y est pas n'arrivera jamais chez l'utilisateur, et ce qui y est sera lisible par tous.