Tutoriels Backend
Lazy loading avec Angular 16
Nous allons implémenter le lazy loading dans notre Application Web.
Nous utiliserons pour cela le framework javascript Angular version 16.2.3
Angular nous permettra de charger les modules à la demande.


Si vous n'avez pas le temps de lire ce guide en entier,
téléchargez le maintenant
Qu'allons nous faire
Nous allons configurer le Lazy loading dans notre Application Web avec Angular version 16.2.3
Il s'agit de l'étape 3 de notre guide Angular qui nous permettra d'obtenir une Application Web de type PWA.
Nous allons utiliser un projet existant dont les caractéristiques sont
- Genéré avec Angular CLI
- Routing
Tous les sources créés sont indiqués en fin de tutoriel.
L' application est à l'adresse suivante
Avant de commencer
La vitesse à laquelle s'affiche un site web est l'un des critères les plus essentiels pour l'utilisateur.
Et cette vitesse s'apprécie en secondes.
Au-delà de 3 secondes 57% des utilisateurs quittent purement et simplement le site.
Quelles méthodes ou techniques doit-on alors utiliser pour que notre site web se charge rapidement ?
L'une des techniques est le lazy loading (“chargement fainéant ou paresseux” en français).
Il a pour effet d'accélérer le fonctionnement d'un site web.
Il permet de spécifier quelles parties d'un site web doivent être chargées lors du démarrage.
Théorie
Avant d'aller plus loin il nous faut comprendre comment fonctionne Angular.
La commande qui nous intéresse concerne la compilation de notre projet.
Dans notre fichier package.json il s'agit de la commande
- ng build
Sans rentrer dans les détails cette commande utilise Webpack (un module bundler).
Grâce à Webpack angular utilise les fichiers de notre projet , les compile pour générer dans le répertoire dist un certain nombre de fichiers que nous pourrons déployer sur un serveur web.
Le projet qui nous sert de base dipose de 6 pages Web
- Home
- About
- Contact
- Login
- Signup
- notfound
La compilation de notre code source génère notamment un fichier main.js qui contient le code de ces 6 pages (ou un fichier du type main.xxxxxxx.js)
Pour vérifier cette théorie il suffit d'ouvrir le fichier dist/angular-starter/main.js faire une recherche sur le code utilisé dans chacune des 6 pages
- home works! (code utilisé dans home.component.html)
- not-found works! (code utilisé dans not-found.component.html)
- contact works! (code utilisé dans contact.component.html)
- login works! (code utilisé dans login.component.html)
- signup works! (code utilisé dans signup.component.html)
- about works! (code utilisé dans about.component.html)
Ce fichier et d'autres seront appelés lors de l'affichage du site Web.
Plus le nombre de pages sera grand, plus le fichier sera volumineux et plus l'affichage sera lent.
Le principe du lazy loading va consister à scinder ce fichier en plusieurs parties qui ne seront chargées qu'en temps voulu.
Passons donc à la pratique.
Création du projet
Plutôt que de tout recréer nous allons utiliser un projet qui contient le routing.
Je vous donne les commandes à lancer , Git est évidemment obligatoire pour récupérer le code source.
# Créez un répertoire demo (le nom est ici arbitraire)
mkdir demo
# Allez dans ce répertoire
cd demo
# Récupérez le code source sur votre poste de travail
git clone https://github.com/ganatan/angular-react-routing
# Allez dans le répertoire qui a été créé
cd angular-react-routing
cd angular
# Exécutez l'installation des dépendances (ou librairies)
npm install
# Exécutez le programme
npm run start
# Vérifiez son fonctionnement en lançant dans votre navigateur la commande
http://localhost:4200/
Pratique
Le lazy loading fonctionne en utilisant la notion de modules et non plus celle des composants.
Nous utiliserons la documentation Angular pour appliquer cette technique.
https://angular.io/guide/lazy-loading-ngmodules
Nous allons adapter notre architecture, en créant un module pour chaque élément à afficher.
Home et not-found resteront gérés de façon classique sous forme de composants.
Utilisons la commande ng generate module que nous offre angular-cli.
# Création des modules (méthode 1)
ng generate module modules/general/contact --routing --module=app
ng generate module modules/general/login --routing --module=app
ng generate module modules/general/signup --routing --module=app
ng generate module modules/general/about --routing --module=app
# Création des modules (méthode 2)
ng g m modules/general/contact --routing --module=app
ng g m modules/general/login --routing --module=app
ng g m modules/general/signup --routing --module=app
ng g m modules/general/about --routing --module=app
Les fichiers nécessaires à chaque composant sont créés automatiquement.
Par exemple pour le composant Contact
- contact-routing.module.ts
- contact.module.ts
Le fichier app.module.ts est automatiquement modifié comme suit.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HomeComponent } from './modules/general/home/home.component';
import { ContactComponent } from './modules/general/contact/contact.component';
import { AboutComponent } from './modules/general/about/about.component';
import { LoginComponent } from './modules/general/login/login.component';
import { SignupComponent } from './modules/general/signup/signup.component';
import { NotFoundComponent } from './modules/general/not-found/not-found.component';
import { AppRoutingModule } from './app-routing.module';
import { MailingComponent } from './modules/general/contact/mailing/mailing.component';
import { MappingComponent } from './modules/general/contact/mapping/mapping.component';
import { WebsiteComponent } from './modules/general/contact/website/website.component';
import { ContactModule } from './modules/general/contact/contact.module';
import { LoginModule } from './modules/general/login/login.module';
import { SignupModule } from './modules/general/signup/signup.module';
import { AboutModule } from './modules/general/about/about.module';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
ContactComponent,
AboutComponent,
LoginComponent,
SignupComponent,
NotFoundComponent,
MailingComponent,
MappingComponent,
WebsiteComponent
],
imports: [
BrowserModule,
AppRoutingModule,
ContactModule,
LoginModule,
SignupModule,
AboutModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Nous devons procéder à certaines modifications sur les fichiers suivants.
- app-routing.module.ts
- app.module.ts
- about-routing.module.ts
- about.module.ts
- contact-routing.module.ts
- contact.module.ts
Le lazy loading sera appliqué sur Contact, About, Login et Signup
Au niveau de AppRoutingModule, nous devons mettre à jour les routes en utilisant loadchildren.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './modules/general/home/home.component';
import { NotFoundComponent } from './modules/general/not-found/not-found.component';
const routes: Routes = [
{ path: '', component: HomeComponent, },
{
path: 'contact',
loadChildren: () => import('./modules/general/contact/contact.module')
.then(mod => mod.ContactModule)
},
{
path: 'about',
loadChildren: () => import('./modules/general/about/about.module')
.then(mod => mod.AboutModule)
},
{
path: 'login',
loadChildren: () => import('./modules/general/login/login.module')
.then(mod => mod.LoginModule)
},
{
path: 'signup',
loadChildren: () => import('./modules/general/signup/signup.module')
.then(mod => mod.SignupModule)
},
{ path: '**', component: NotFoundComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
declarations: []
})
export class AppRoutingModule { }
Il nous faut modifier le module AppModule et ne laisser que les composants HomeComponent et NotFoundComponent
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { HomeComponent } from './modules/general/home/home.component';
import { NotFoundComponent } from './modules/general/not-found/not-found.component';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
NotFoundComponent
],
imports: [
BrowserModule,
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Il ne reste plus qu'à modifier les fichiers de routing et module pour Contact, Login, Signup et About.
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AboutComponent } from './about.component';
const routes: Routes = [
{ path: '', component: AboutComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AboutRoutingModule { }
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AboutComponent } from './about.component';
import { AboutRoutingModule } from './about-routing.module';
@NgModule({
imports: [
CommonModule,
AboutRoutingModule
],
exports: [
AboutComponent
],
declarations: [
AboutComponent
],
providers: [
],
})
export class AboutModule { }
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ContactComponent } from './contact.component';
const routes: Routes = [
{ path: '', component: ContactComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ContactRoutingModule { }
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ContactComponent } from './contact.component';
import { ContactRoutingModule } from './contact-routing.module';
@NgModule({
imports: [
CommonModule,
ContactRoutingModule
],
exports: [
ContactComponent
],
declarations: [
ContactComponent
],
providers: [
],
})
export class ContactModule { }
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { SignupComponent } from './signup.component';
const routes: Routes = [
{ path: '', component: SignupComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class SignupRoutingModule { }
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SignupComponent } from './signup.component';
import { SignupRoutingModule } from './signup-routing.module';
@NgModule({
imports: [
CommonModule,
SignupRoutingModule
],
exports: [
SignupComponent
],
declarations: [
SignupComponent
],
providers: [
],
})
export class SignupModule { }
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './login.component';
const routes: Routes = [
{ path: '', component: LoginComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class LoginRoutingModule { }
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LoginComponent } from './login.component';
import { LoginRoutingModule } from './login-routing.module';
@NgModule({
imports: [
CommonModule,
LoginRoutingModule
],
exports: [
LoginComponent
],
declarations: [
LoginComponent
],
providers: [
],
})
export class LoginModule { }
Vérification
Pour vérifier la théorie du lazy loading nous devons effectuer une nouvelle compilation (npm run build)
Dans le répertoire dist/angular-starter nous obtenons cette fois 5 fichiers
- main.js
- modules-general-about-about-module.js
- modules-general-contact-contact-module.js
- modules-general-login-login-module.js
- modules-general-signup-signup-module.js
Remarque:
Les noms peuvent être différents notamment avec des numéros c'est webpack qui gère le nommage.
Le code de chacune de nos 5 pages est maintenant disposé de la façon suivante
- home works! (code trouvé dans main.js)
- not-found works! (code trouvé dans main.js)
- contact works! (code trouvé dans modules-general-contact-contact-module.js)
- login works! (code trouvé dans modules-general-login-login-module.js)
- signup works! (code trouvé dans modules-general-signup-signup-module.js)
- about works! (code utilisé modules-general-about-about-module.js)
Si nous exécutons l'application (npm run start) nous pouvons voir dans Chrome (F12) au niveau de l'onglet Network comment les fichiers sont chargés.
- Au lancement du site : main.js est appelé.
- A la sélection de About : modules-general-about-about-module.js est appelé une seule fois
- A la sélection de login: modules-general-login-login-module.js est appelé une seule fois
- A la sélection de signup: modules-general-signup-signup-module.js est appelé une seule fois
- A la sélection de Contact : modules-general-contact-contact-module.js est appelé une seule fois
Si nous lançons l'url localhost/contact
- Dans ce cas main.js et seulement modules-general-contact-contact-module.js sont appelés
Conclusion :
Quelle que soit le nombre de pages, le fichier main.js aura toujours la même taille.
Le lancement du site qui charge le fichier main.js se fera toujours à la même vitesse.
Child Routes
Cette application contient aussi la gestion des Child routes.
Cette question est évoquée dans la documentation
https://angular.io/guide/router#child-route-configuration
Vous retrouverez dans le dépôt sur github l'ajout de la notion de routing avec Children.
Dans le tutoriel sur le routing trois composants ont été rajoutés dans Contact
- mailing
- mapping
- website
Il nous faut rajouter la notion de modules sur ces composants.
7 fichiers seront notamment modifiés pour en prendre compte.
- contact-routing.module.ts
- mailing-routing.module.ts
- mapping-routing.module.ts
- website-routing.module.ts
- app.module.ts
# Rajout du module mailing
ng generate module modules/general/contact/mailing --routing --module=app
# Rajout du module mapping
ng generate module modules/general/contact/mapping --routing --module=app
# Rajout du module website
ng generate module modules/general/contact/website --routing --module=app
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ContactComponent } from './contact.component';
const routes: Routes = [
{
path: '', component: ContactComponent, children: [
{
path: '',
loadChildren: () => import(`./mailing/mailing.module`)
.then(mod => mod.MailingModule)
},
{
path: 'mailing',
loadChildren: () => import(`./mailing/mailing.module`)
.then(mod => mod.MailingModule)
},
{
path: 'mapping',
loadChildren: () => import(`./mapping/mapping.module`)
.then(mod => mod.MappingModule)
},
{
path: 'website',
loadChildren: () => import(`./website/website.module`)
.then(mod => mod.WebsiteModule)
},
{
path: '**',
loadChildren: () => import(`./mailing/mailing.module`)
.then(mod => mod.MailingModule)
},
]
},
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ContactRoutingModule { }
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { MailingComponent } from './mailing.component';
const routes: Routes = [
{ path: '', component: MailingComponent, children: [] }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class MailingRoutingModule { }
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { MappingComponent } from './mapping.component';
const routes: Routes = [
{ path: '', component: MappingComponent, children: [] }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class MappingRoutingModule { }
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { WebsiteComponent } from './website.component';
const routes: Routes = [
{ path: '', component: WebsiteComponent, children: [] }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class WebsiteRoutingModule { }
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HomeComponent } from './modules/general/home/home.component';
import { NotFoundComponent } from './modules/general/not-found/not-found.component';
import { AppRoutingModule } from './app-routing.module';
@NgModule({
declarations: [
AppComponent,
HomeComponent,
NotFoundComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Tests
Il ne reste plus qu' à tester l'application.
# Développement
npm run start
http://localhost:4200/
# Tests
npm run lint
npm run test
# Production
npm run build
Code source
Le code source utilisé en début de tutoriel est disponible sur github
https://github.com/ganatan/angular-react-routing
Le code source obtenu à la fin de ce tutoriel est disponible sur github
https://github.com/ganatan/angular-react-lazy-loading
Les étapes suivantes vous permettront d'obtenir une application prototype.
- Etape 4 : Bootstrap avec Angular
- Etape 5 : Modules avec Angular
- Etape 6 : Server Side Rendering avec angular
- Etape 7 : Progressive Web App avec Angular
- Etape 8 : Search Engine Optimization avec Angular
- Etape 9 : HttpClient avec Angular
Les étapes suivantes vous permettront d'améliorer ce prototype
- Components avec Angular
- Services avec Angular
- Template Driven Forms avec Angular
- Charts avec Angular
Cette dernière étape permet d'obtenir un exemple d'application
Le code source de cette application finale est disponible sur GitHub
https://github.com/ganatan/angular-app
Comment créer une application From scratch ?
Créez votre compte ganatan
Téléchargez gratuitement vos guides complets
Démarrez avec angular CLI 
Gérez le routing 
Appliquez le Lazy loading 
Intégrez Bootstrap 
Utilisez Python avec Angular 
Utilisez Django avec Angular 
Utilisez Flask avec Angular 
