Boost Angular’s Performance by Lazy Loading your Modules

Chidume Nnamdi 🔥💻🎵🎮
Bits and Pieces
Published in
11 min readDec 17, 2018

--

In this article, we will talk about lazy loading in Angular.

At the end of this article, we will gain a valuable insight into how to boost the performance of our Angular apps by loading our ngModules on demand.

We will need several tools in this article, Node.js and NPM. As this is an Angular project, it depends heavily on Node.js and NPM. As NPM bundles with Node.js, installing Node.js adds NPM to our machine.

We will not go through the process of installing Node.js, you can refer to the installation instructions. Instead, let’s dive into boosting your performance.

Tip: When using a component-based framework like Angular or React, use tools like Bit to easily share, reuse and sync your components across projects- to build faster with your team. It’s free, give it a try.

What is Lazy Loading?

lazy loading is the process of loading modules(images, videos, documents, JS, CSS, etc) on-demand.

The most important concepts of application performance are: Response Time, and Resources Consumption. It is inevitable that they are going to happen. Also, a problem can arise from anywhere though, but it is highly important to find and address them before they happen.

The prospect of Lazy Loading in Angular helps reduce the risk of some of the web app performance problems to a minimal. Lazy Loading does well to check the concepts we listed above:

  1. Response Time: This is the amount of time it takes the web application to load and the UI interface to be responsive to users. Lazy loading optimizes response time by code splitting and loading the desired bundle.
  2. Resources Consumption: Humans are impatient creatures if a website takes more than 3 seconds to load, 70% of us will give up. Web apps should not take this long to load. So, to reduce the amount of resources loading, lazy loading loads the code bundle necessary at a time.

Lazy loading speeds up our application load time by splitting it into multiple bundles and loading them on demand.

Advantages of lazy loading:

  • High performance in bootstrap time on initial load.
  • Modules are grouped according to their functionality.
  • Smaller code bundles to download on initial load.
  • Activate/download a code module by navigating to a route.

How Lazy Loading works?

Lazy Loading generally, is a concept where we delay loading of an object until it is needed.

In Angular, all the JavaScript components declared in the declarations array app.module.ts are bundled and loaded in one fell swoop when a user visits our site.

Let’s say we have an app that has three components: Home, About, and ViewDetails. Also, it has three routes:

const routes: Routes = [
{
path: '',
component: HomeComponent
},
{
path: 'about',
component: AboutComponent
},
{
path:'viewdetails',
component: ViewDetailsComponent
}
]
@NgModule({
declarations: [
HomeComponent,
AboutComponent,
ViewDetailsComponent
]
})

We see from the above code, different router states our app can be in. A different route to a different state.

The issue here is that Angular bundles up all the components in the declarations array and loads all of the components. It doesn't make any sense to load all the components when the user is only navigating to / which should only load the corresponding component, HomeComponent.

Lazy loading was implemented in the Angular router (@angular/router) to fix this. The entire code bundle is code split, and the router, lazily load the code chunks on demand.

Let’s start Lazy Loading

To demonstrate lazy loading, we are going to create an Angular app. The app will have three components:

  • Home: This will be our index page, it will be activated by the route “/”.
  • ViewDetails: This component will display the info about a product. It will be assigned to the route, “/viewdetails”.
  • About: This is our about page, it will display our info as a company. It will be assigned to the route: “/route”.

We are going to lazy load components About, and ViewDetails. They are going to have their own feature modules, that will be activated using @angular/router's loadChildren property. The Home component will be the index page.

Project Setup

Here, we set up our project. We will be using the Angular CLI (ng) utility throughout in this article. If you don't have it installed. Run the following command to install it:

npm install @angular/cli -g

With the -g flag, we instructed NPM to make it global, so that we can use the tool from any directory in our system.

Run the following to create a barebones Angular app:

ng new ng-lazy-load --minimal

Notice the --minimal flag in our command. It tells the ng command-line tool to create our Angular app without all the *.spec.ts, *.css, *.html files. Everything will be inline i.e Components will contain everything (CSS, HTML) in one file.

Let’s take a look at our app.component.ts file:

// src/app/component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<p>
app works!
</p>
`,
styles: []
})
export class AppComponent {
title = 'app';
}

You see, we have inline styles and templates. All our HTML goes into the template property, also, our CSS into the stylesproperty.

Scaffolding Angular apps with the minimal flag also configures the .angular-cli.json file, to create future components without the accompanying *.spec.ts and *.css and *.html files.

Our project directory(ng-lazy-load) will look like this:

+- ng-lazy-load
+- src
+- app
+- app.component.ts
+- app.module.ts
+- assets
+- .gitkeep
+- environment
+- environment.prod.ts
+- environment.ts
+- index.html
+- main.ts
+- polyfills.ts
+- style.css
+- tsconfig.app.json
+- typings.d.ts
+- .angular-cli.json
+- .gitignore
+- package.json
+- tsconfig.json

You see like we said earlier only component files are created. No tests(*.spec.ts) are created. No CSS or HTML files.

Creating Angular apps this way is perfect for simple examples and demo purposes.

Creating the components

Now, we will create our components. To create the Home component, run the following command:

ng g c home  create src/app/home/home.component.ts (250 bytes)
update src/app/app.module.ts (390 bytes)

The ng tool has a plethora of commands to use to make development easy and stress-free. Here we used the shortcut notation of ng generate component.

ng g c is the same as typing ng generate component. ng generate component is used to add a component to an existing app. The generate command have different sub-commands that can be used to add different features to an Angular app.

Here, the ng g c home command:

  • Creates src/app/home directory
  • Inside that directory, it generates home.component.ts file and updates app.module.ts to import the Home component and add it to its declarations array.

It generates only one file home.component.ts without the extra *.spec.ts. *.css, and *.html files because of the configuration in the .angular-cli.json, we mentioned earlier.

Next, we create the About module and component. If you noticed the Home component has no module because it will not be lazily loaded. Only components activated by lazy loading should have a module.

So, run the following commands to create an About module and component:

ng g m about --routing
create src/app/about/about-routing.module.ts (248 bytes)
create src/app/about/about.module.ts (275 bytes)
ng g c about
create src/app/about/about.component.ts (253 bytes)

Notice another new command ng g m. This command is the shortcut notation for ng generate module. It adds a module to our app. --routing flag was added in our command to tell the ng tool to create the module with a routing system.

So, our command ng g m about --routing:

  • creates a directory src/app/about
  • inside that directory, about-routing.module.ts and app.module.ts files are created.

about-routing.module.ts file holds the routing information of the module and it is imported in about.module.ts in its imports array.

Next, we finish off by creating the last component and module, ViewDetails:

ng g m viewdetails --routing
create src/app/viewdetails/viewdetails.module.ts (299 bytes)
create src/app/viewdetails/viewdetails-routing.module.ts (254 bytes)
ng g c viewdetails
create src/app/viewdetails/viewdetails.component.ts (271 bytes)
update src/app/viewdetails/viewdetails.module.ts (383 bytes)

Creating a lazy-loaded module

We have actually created our lazy loaded modules above, here we will implement routing for each of the modules.

About module

As we noticed above during its creation, two modules were created, about.routing.module.ts and about.module.ts.about.routing.module.ts holds the routes of the module and it is imported in the imports array of about.module.ts.

We will configure our routes in about-routing.module.ts, by inserting objects inside the routes array:

// src/app/about/about-routing.module.ts
...
const routes: Routes = []
...

The routes: Routes array is used to define all the possible router states an app could be in. It takes an object with two basic properties: path and component.

Insert the following inside it:

// src/app/about/about-routing.module.ts
...
import { AboutComponent } from "./about.component";
const routes: Routes = [
{
path: "",
component: AboutComponent
}
]
...

Here, we used an empty path to denote default/index route. We imported AboutComponent, so that we can assign it to the component property in the routes array. The @angular/router uses the component property to know which component to load on the browser DOM.

ViewDetails module

We will do the same thing as we did above. import the ViewDetails component and configure the routes:

// src/app/viewdetails/viewdetails-routing.module.ts
...
import { ViewdetailsComponent } from './viewdetails.component';
const routes: Routes = [
{
path: "",
component: ViewdetailsComponent
}
]
...

Lazy loading the new module

We are done with our feature modules. Now, we set up the parent module, app.module.ts.

We import the Home component as it will be used in route configuration:

// src/app/app.module.ts
...
import { HomeComponent } from './home/home.component';
...

Next, we import the Routes and RouterModule:

// src/app/app.module.ts
...
import { HomeComponent } from './home/home.component';
import { Routes, RouterModule } from '@angular/router';
...

OK, we now create routes variable that will hold our routes array, just like we saw in our feature modules:

// src/app/app.module.ts
...
import { HomeComponent } from './home/home.component';
import { Routes, RouterModule } from '@angular/router';
const routes: Routes = [
{
path: "",
component: HomeComponent
},
{
path: "viewdetails",
loadChildren: "app/viewdetails/viewdetails.module#ViewdetailsModule"
},
{
path: "about",
loadChildren: "app/about/about.module#AboutModule"
}
]
...

So, here we made Home component the default route by using the empty path:"". Looking at the next two routes, you'll notice something strange there, there is an odd-looking syntax with loadChildren, then followed by # with a module name. These routes are the routes we want to be lazy-loaded and were set up in feature modules: AboutModule and ViewdetailsModule.

The loadChildren property is how we tell Angular to lazy load a module. The first part before the # points to the relative path of the feature module. The second part will be the class name of the feature module. Also, if you noticed, we didn't import the feature modules, it is left for the parent module, AppModule to dictate their URLs and somehow import the modules.

This is how feature modules are lazy loaded in Angular:

loadChildren: "PATH_TO_FEATURE_MODULE_#_FEATURMODULE_CLASS_NAME"

There are a few important things to note here:

  1. The loadChildren property is used instead of component.
  2. We defined not only the path but, also the name of the feature module class.

Testing our App

Now, we have our app all set up and all our features modules correctly implemented. We need to test the app, to see them in action. We will do one last thing before we test.

  • If you’re using Bit, you can also test individual components in the cloud.

We have to create a router-outlet in app.component.ts to tell Angular where to render our components. Open up app.component.ts and add the following to the template tag:

// src/app/app.component.ts
...
template: `
<!--The content below is only a placeholder and can be replaced.-->
<a routerLink="/viewdetails">Dashboard</a> |
<a routerLink="/about">About</a>
<router-outlet></router-outlet>
`
...

We added the <router-outlet></router-outlet> tag and also added some links to navigate to About and ViewDetailscomponents.

Now, we serve our app:

ng serve

If you noticed in your terminal, there are extra code bundles generated apart from the normal bundles:

  • inline.bundle.js
  • main.bundle.js
  • styles.bundle.js
  • polyfills.bundle.js
  • vendor.bundle.js
webpack: Compiling...
Date: 2018-04-21T15:42:51.440Z
Hash: 2a089415b0d05889be86
Time: 1039ms
chunk {about.module} about.module.chunk.js () 10.2 kB
chunk {inline} inline.bundle.js (inline) 5.79 kB [entry]
chunk {main} main.bundle.js (main) 24.2 kB [initial] [rendered]
chunk {polyfills} polyfills.bundle.js (polyfills) 555 kB [initial]
chunk {styles} styles.bundle.js (styles) 34.1 kB [initial]
chunk {vendor} vendor.bundle.js (vendor) 8.07 MB [initial]
chunk {viewdetails.module} viewdetails.module.chunk.js () 10.8 kB

Angular generated extra bundles about.module.chunk.js and viewdetails.module.chunk.js. These are the feature modules we configured to be loaded on demand. These extra module chunks will be loaded when either of the routes /about and /viewdetails is navigated to.

Navigate to http://localhost:4200 on your browser of choice. Open JavaScript Console > Network.

NB: I would advise using Google Chrome browser.

Here, you will see JS, HTML, CSS etc files that are loaded by the application. You can filter files you want to see, by clicking on the Filter tab.

You see that inline.bundle.js, main.bundle.js, styles.bundle.js, polyfills.bundle.js,vendor.bundle.js are all loaded initially. Viewdetails and About modules were not loaded, they will if we navigate to their routes.

OK, now we want to go to /viewdetails link. Remember, the /viewdetails route is configured to lazy load (load on demand) so it didn't load when we navigated to the default route(/) above. Click on viewdetails link on the app:

You see viewdetails.module.chunk.js being loaded in the Network tab.

To see the same thing happen on our About module. Click on the About link.

Note, once a lazy loaded feature module is loaded, it doesn’t get re-loaded when it’s route is navigated to again.

That’s it, lazy loading has been successfully achieved in Angular.

Conclusion

We have seen how important it is, to lazy-load modules in Angular and also, how it effectively improves web app performance.

Lazy loading was easily achieved by using the loadChildren property.

Let’s recap on the advantages of lazy loading in Angular:

  • Lazy loading helps decrease load time (faster download speeds).
  • Modules are loaded on demand.
  • Modules are loaded when the user navigates to their routes.
  • Lazy loading decreases resources consumption (lower resource costs).
  • Lazy loading doesn’t load everything once, it loads only what the user expects to see first.

That’s it. Please, feel free to ask if you have any questions or comments in the comment section.

This GitHub repository contains the final code developed throughout this article!

Thanks !!!

--

--

JS | Blockchain dev | Author of “Understanding JavaScript” and “Array Methods in JavaScript” - https://app.gumroad.com/chidumennamdi 📕