Application performance
Angular Patterns SPA Typescript

Improving Angular Performance with the Lazy Loading Design Pattern

Welcome to today’s post.

In today’s post I will be showing how we can improve performance within an Angular application by using the Angular an interface design pattern known as Lazy Loading.

In a previous post I explained what feature modules were by showing how to refactor Angular components into feature modules.

In a previous post I showed how to improve performance when loading Angular components by pre-loading and synchronizing data into components by using resolvers.

The default loading pattern in Angular is to use Eager Loading. What this does is to pre-load all within the default AppModule and other NgModules modules. If there are many components within the modules, then loading can take a considerable time.

What Lazy Loading does is to load feature modules when they are needed.

Before I show how to use Lazy Loading method of module loading, I will show how the default Eager loading behavior is implemented within an Angular application loading module.

Default Eager Loading Behavior of an NgModule

To eager load an NgModule module we import the module into the AppModule configuration file as shown:

import { ComponentsModule } from './components/components.module';

@NgModule({
  declarations: [
	…
  ],
  imports: [
	…
    ComponentsModule,
	…
  ],

The components.module.ts source contains imports of components within our module that are exported and visible to the application module.

import { NgModule } from '@angular/core';

import { Component1 } from '../components/component1.component';
import { Component2 } from '../components/component2.component';
import { Component3 } from '../components/component3.component';
…
import { ComponentN } from '../components/componentN.component';

@NgModule({
  declarations: [
    Component1,
    Component2,
    Component3,
	…
    ComponentN
  ],
  imports: [
    CommonModule,
	…
  ],
  exports: 
  [
    Component1,
    Component2,
    Component3,
	…
    ComponentN
  ]
})
export class ComponentsModule { }

The module will then be visible to and usable by application components.

In our app-routing module app-routing.module.ts, in order for the components in our feature module to have accessible URL routes throughout application components, the feature module components will need to be imported into the routing module as shown:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  { path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
  { path: '', redirectTo: 'home', pathMatch: 'prefix'},
  { path: 'component1, component: Component1 },    
  { path: 'component2, component: Component2 },    
  { path: 'component3, component: Component3 },    
	…
  { path: 'componentN, component: ComponentN }
 …
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

When feature modules are structured as with the above, they are loaded at the same time the app module is loaded.

To access one of our components within the component feature module we could make a navigational call from a HTML template hyperlink such as:

<a routerLink="/component1" routerLinkActive="activebutton">
  Demo 1
</a>

Or by using the Router class Navigate() method from within an application components event as shown:

eventComponent(event)
{
    this.router.navigate([component1']);     
    console.log("go to first component1.");
}

In the next section, I will show how to implement lazy loading of feature modules within an Angular application.

Implementation of Lazy Loading Behavior of NgModules

To implement lazy loading of our feature modules, we will need to make some small adjustments to our loading pattern in the routing module.

I will show how this is done.

To load a feature module when the route is activated, we use loadChildren and the import() function as shown in the Routes array in the app routing module:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  { path: 'home', component: HomeComponent, canActivate: [AuthGuard] },
  { path: '', redirectTo: 'home', pathMatch: 'prefix'},

  { path: 'components', loadChildren: () => 
    import('./components/components.module')
        .then(m => m.ComponentsModule) },
  …
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

What this does is to add navigation to our feature module so that it is only loaded when navigation to the full path to the component is actioned within any of our application components.

To load and display one of our components within the component feature module we could make a navigational call from a HTML template hyperlink such as:

<a routerLink="/components/component1" routerLinkActive="activebutton">
  Demo 1
</a>

Or by using the Router class Navigate() method from within an application components event as shown:

eventComponent(event)
{
    this.router.navigate(['components/component1']);     
    console.log("go to first component1.");
}

Notice that the navigational relative path has to be fully qualified with the name of the feature module, components leading the path name, and ending with the name of the component that is to be accessed.

When the application is rebuilt and is run the lazy loading of feature module can be confirmed when the component is used within in our application menu as shown:

We have seen how to utilise lazy loading of child components within an Angular application. With the default eager loading method, the feature components are loaded during application startup so that all feature modules are available for use after the application has initialized. What this does is to load all feature modules into memory and can take a considerable amount of time and memory.

The loading of components using lazy loading only loads feature modules when the router link to a component within a feature module is accessed within the application. Any components with feature modules that are not accessed within the application, are not loaded, and so the memory usage is saved.   

The lazy loading of components yields gains in application performance as the loading only occurs when the components are needed instead of on application start-up.

That is all for today’s post.

I hope you found this post useful and informative.

Social media & sharing icons powered by UltimatelySocial