Lazy loading with Angular 10

15/09/20 dannyVersion Française

What are we going to do ?

We will implement the Lazy loading in our Web Application.
We will use the Angular version 10.1.1 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 5 web pages

  • Home
  • About
  • Contact
  • Signin
  • notfound

The compilation of our source code generates one file : main.js that contain the code of these 5 pages.
To check this theory just open the file dist/angular-starter/ main.js, search the code used in each of the 5 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)
  • signin works! (code used in signin.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/signin --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/signin --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.

src/app/app.module.ts
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 { ContactComponent } from './modules/general/contact/contact.component';
import { AboutComponent } from './modules/general/about/about.component';
import { SigninComponent } from './modules/general/signin/signin.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 { SigninModule } from './modules/general/signin/signin.module';
import { AboutModule } from './modules/general/about/about.module';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent,
    ContactComponent,
    AboutComponent,
    SigninComponent,
    NotFoundComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ContactModule,
    SigninModule,
    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 and Signin
At the level of AppRoutingModule, we need to update the routes using loadchildren.

src/app/app-routing.module.ts
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: 'signin',
    loadChildren: () => import('./modules/general/signin/signin.module')
      .then(mod => mod.SigninModule)
  },
  { 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

src/app/app.module.ts
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 { }


All that remains is to modify the routing and module files for Contact, Signin and About.

src/app/modules/general/about/about-routing.module.ts
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 { }

src/app/modules/general/about/about.module.ts
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 { }
src/app/modules/general/contact/contact-routing.module.ts
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 { }
src/app/modules/general/contact/contact.module.ts
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 { }
src/app/modules/general/signin/signin-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { SigninComponent } from './signin.component';

const routes: Routes = [
  { path: '', component: SigninComponent },
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class SigninRoutingModule { }
src/app/modules/general/signin/signin.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { SigninComponent } from './signin.component';
import { SigninRoutingModule } from './signin-routing.module';

@NgModule({
  imports: [
    CommonModule,
    SigninRoutingModule
  ],
  exports: [
    SigninComponent
  ],
  declarations: [
    SigninComponent
  ],
  providers: [
  ],
})
export class SigninModule { }

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 4 files

  • main.js
  • modules-general-about-about-module.js
  • modules-general-contact-contact-module.js
  • modules-general-signin-signin-module.js

The code of each of our 4 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)
  • signin works! (code found in modules-general-signin-signin-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 Signin: modules-general-signin-signin-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