Lazy loading and Angular routing

Maurice de Beijer
5 min readJan 2, 2017

--

One problem with creating a Single Page Application (SPA) is that you might load the entire application when the user first starts the application but most of it is never used. Sure navigation is nice and fast after the initial load but that slows down the initial load as that becomes larger. And that might be worth it if the user navigates to all, or most, routes loaded. But when most routes are never activated that is just a big waste. Fortunately with Angular that is easy enough to fix with lazy loading. And lazy loading is really easy to set up in Angular as well.

BTW: This blog post is about Angular 2 but as we are not supposed to use the 2 anymore it’s just Angular :-)

Eager loading of routes

Below an example of eager loading with Angular routes. Notice there is no HTTP traffic at all when I click on the different links and different routes are activated. Of course this is a really simple example and in a real application you would most likely be fetching some data using an AJAX call

So what code did I use here?

Most of it was just generated with the Angular CLI. You can see all the code here but it boils down to the main AppModule with two sub modules: Mod1Module and Mod2Module both generated with the CLI and each has a single route and component added to it. The Mod1Module is about books and Mod2Module is about authors. Below is Mod1Module as an example.

1: import { NgModule } from '@angular/core';2: import { CommonModule } from '@angular/common';3: import { Routes, RouterModule } from '@angular/router';4:5: import { BooksComponent } from './books/books.component';6:7: const routes: Routes = [8:   { path: 'books', component: BooksComponent },9: ];10:11: @NgModule({12:   imports: [13:     CommonModule,14:     RouterModule.forChild(routes)15:   ],16:   declarations: [BooksComponent],17: })18: export class Mod1Module { }

The AppModule is also quite straightforward. It adds the two sub modules and initializes the routing module. As both routes used are defined in the sub modules the routing table is actually empty here.

1: import { BrowserModule } from '@angular/platform-browser';2: import { NgModule } from '@angular/core';3: import { FormsModule } from '@angular/forms';4: import { HttpModule } from '@angular/http';5: import { Routes, RouterModule } from '@angular/router';6:7: import { AppComponent } from './app.component';8:9: import { Mod1Module } from './mod1/mod1.module';10: import { Mod2Module } from './mod2/mod2.module';11:12: const routes: Routes = [];13: const routerModule = RouterModule.forRoot(routes);14:15: @NgModule({16:   declarations: [17:     AppComponent18:   ],19:   imports: [20:     BrowserModule,21:     FormsModule,22:     HttpModule,23:     routerModule,24:     Mod1Module,25:     Mod2Module26:   ],27:   providers: [],28:   bootstrap: [AppComponent]29: })30: export class AppModule { }

The view for the main AppComponent is also quite simple. There are two links with anchor tags using the RouterLink directive to let the user navigate. Finally there is the RouterOutlet directive to show the route components template.

1: <h1>2: {{title}}3: </h1>4:5: <nav>6: <a routerLink="/books">Books</a>7: <a routerLink="/authors">Authors</a>8: </nav>9:10: <hr />11:12: <router-outlet>13: </router-outlet>

Running this with ng serve works and as you can see above the application loads everything at startup, no additional requests are done when we click on the route links.

Switching to lazy loading

It turns out that switching to lazy loading is really simple and just requires a few small changes.

We want the sum modules only to be loaded when needed so the main routes need to be defined in AppModule instead. In both Mod1Module and Mod2Module we need to change the route path to an empty string. The only change is on line 8 here.

1: import { NgModule } from '@angular/core';2: import { CommonModule } from '@angular/common';3: import { Routes, RouterModule } from '@angular/router';4:5: import { BooksComponent } from './books/books.component';6:7: const routes: Routes = [8:   { path: '', component: BooksComponent },9: ];10:11: @NgModule({12:   imports: [13:     CommonModule,14:     RouterModule.forChild(routes)15:   ],16:   declarations: [BooksComponent],17: })18: export class Mod1Module { }

The majority of the work needs to be done in AppModule. Still it is just a few lines and most of it is deleting code :-)

1: import { BrowserModule } from '@angular/platform-browser';2: import { NgModule } from '@angular/core';3: import { FormsModule } from '@angular/forms';4: import { HttpModule } from '@angular/http';5: import { Routes, RouterModule } from '@angular/router';6:7: import { AppComponent } from './app.component';8:9: const routes: Routes = [10:   { path: 'books', loadChildren: './mod1/mod1.module#Mod1Module' },11:   { path: 'authors', loadChildren: './mod2/mod2.module#Mod2Module' },12: ];13: const routerModule = RouterModule.forRoot(routes);14:15: @NgModule({16:   declarations: [17:     AppComponent18:   ],19:   imports: [20:     BrowserModule,21:     FormsModule,22:     HttpModule,23:     routerModule24:   ],25:   providers: [],26:   bootstrap: [AppComponent]27: })28: export class AppModule { }

Note that both the TypeScript as well as the module imports for Mod1Module and Mod2Module are gone. New are the two routes where the books and the authors are defines. Instead of specifying the component to load there is a string ‘./mod1/mod1.module#Mod1Module’ which points to the module file and the module class to load. This is in a string because we don’t want to import the type, they should be in a separate module and only loaded when needed. The documentation here is still a bit sparse as it point to the LoadChildren section which points back to Routes :-(

Oh well, the documentation is open source so I guess I should open a pull request :-)

Anyway, with these small changes both Mod1Module and Mod2Module are lazily loaded when first needed and not on the initial page load. Below you can see 0.chunk.js and 1.chunk.js being loaded the first time I click on a link to the respective module.

You can see the compete commit with all the changes here or the GitHub repository with the final code here.

Enjoy!

Originally published at blogs.msmvps.com on January 2, 2017.

--

--