Lazy loading with Angular 14
What are we going to do?
We will implement the Lazy loading in our Web Application.
We will use the Angular version 14.0.4 javascript framework.
This is Step 3 of our Angular guide which will allow us to obtain a PWA Web Application.
We will use an existing project whose characteristics are
- Managed with Angular CLI
- Routing
All created sources are indicated at the end of the tutorial.
The application is at the following address
Before you start
The speed at which a website is displayed is one of the most important criteria for the user.
And this speed is appreciated in seconds.
Beyond 3 seconds, 57% of users leave the site altogether.
What methods or techniques should we use to make our website load quickly?
One of the techniques is the lazy loading.
It has the effect of speeding up the operation of a website.
It allows you to specify which parts of a website should be loaded at startup.
Theory
Before going further we need to understand how Angular works.
The command that interests us concerns the compilation of our project.
In our package.json file it is the command
- ng build
Without going into details this command uses Webpack (a bundler module).
With Webpack angular uses the files of our project, compiles them to generate in the dist directory a number of files that we can deploy on a web server.
The project uses 6 web pages
- Home
- About
- Contact
- Login
- Signup
- notfound
The compilation of our source code generates one file : main.js that contain the code of these 6 pages.
To check this theory just open the file dist/angular-starter/ main.js, search the code used in each of the 6 pages
- home works! (code used in home.component.html)
- not-found works! (code used in not-found.component.html)
- contact works! (code used in contact.component.html)
- login works! (code used in login.component.html)
- signup works! (code used in signup.component.html)
- about works! (code used in about.component.html)
This file will be called when viewing the website.
The larger the number of pages, the larger the file and the slower the display.
The principle of lazy loading will consist in splitting this file in several parts which will be loaded only in due time.
So let's move on to practice.
Practice
The lazy loading works by using the notion of modules and not that of components.
We will use the Angular documentation to apply this technique.
https://angular.io/guide/lazy-loading-ngmodules
We will adapt our architecture, creating a module for each element to display.
Home and not-found will continue to be managed conventionally as components.
Let us use the command ng generate module that we offer angular-cli.
# Creating modules
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
# Creating modules (method 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
The files needed for each component are created automatically.
For example for the Contact component
- contact-routing.module.ts
- contact.module.ts
The app.module.ts file is automatically modified as follows.
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 { 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
],
imports: [
BrowserModule,
AppRoutingModule,
ContactModule,
LoginModule,
SignupModule,
AboutModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
We need to make some changes to the following files.
- app-routing.module.ts
- app.module.ts
- about-routing.module.ts
- about.module.ts
- contact-routing.module.ts
- contact.module.ts
The lazy loading will be applied on Contact, About, Login and Signup
At the level of AppRoutingModule, we need to update the routes using 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 { }
We need to modify the AppModule module and leave only the components HomeComponent and 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 { }
All that remains is to modify the routing and module files for Contact, Login, Signup and 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
To check the theory of lazy loading we need to perform a new compilation (npm run build)
In the dist/angular-starter directory we get this 5 files
- 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
The code of each of our 5 pages is now arranged as follows.
- home works! (code found in main.js)
- not-found works! (code found in main.js)
- contact works! (code found in modules-general-contact-contact-module.js)
- login works! (code found in modules-general-login-login-module.js)
- signup works! (code found in modules-general-signup-signup-module.js)
- about works! (code used in modules-general-about-about-module.js)
If we run the application (npm run start) we can see in Chrome (F12) on the Network tab how the files are loaded.
- At the launch of the site: main.js is called.
- At selection of About: modules-general-about-about-module.js is called only once
- At selection of Login: modules-general-login-login-module.js is called only once
- At selection of Signup: modules-general-signup-signup-module.js is called only once
- To the selection of Contact: modules-general-contact-contact-module.js is called only once
If we launch the url localhost / contact
- In this case main.js and only modules-general-contact-contact-module.js are called
Conclusion :
Regardless of the number of pages, the main.js file will always be the same size.
Launching the site that loads the main.js file will always be at the same speed.
Tests
All that remains is to test the application.
# Development
npm run start
http://localhost:4200/
# Tests
npm run lint
npm run test
npm run e2e
# Production
npm run build
Source code
The source code used at the beginning of the tutorial is available on github
https://github.com/ganatan/angular-react-routing
The source code obtained at the end of this tutorial is available on github
https://github.com/ganatan/angular-react-lazy-loading
The tutorials for obtaining the application are as follows
- Step 1 : Getting started with Angular
- Step 2 : Routing with Angular
- Step 3 : Lazy loading with Angular
- Step 4 : Bootstrap with Angular
- Step 5 : Server Side Rendering with Angular
- Step 6 : HttpClient with Angular
- Step 7 : Transfer State with Angular
- Step 8 : Progressive Web App with Angular
- Step 9 : Search Engine Optimization with Angular
The source code of the application is available on github
https://github.com/ganatan/angular-app