Angular has this cool feature of loading a module lazily. Modules that are set up to load lazily can significantly save application startup time. Lazy-load module set up is done in the application’s routing configuration section.
As the title suggests, we will be using the AngularSPA
template shipped with Visual Studio 2017 Preview (2) for demonstration.
Route that is configured to lazy-load a module sends an HTTP GET
to the server which in turn returns the module in a chunk of code block. This only happens when the router is activated for the first time in application lifecycle.
Here’s how the AppModuleShared
(app.module.shared.ts) is setup in the AngularSPA
starter project:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './components/app/app.component';
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
import { CounterComponent } from './components/counter/counter.component';
@NgModule({
declarations: [
AppComponent,
NavMenuComponent,
CounterComponent,
FetchDataComponent,
HomeComponent
],
imports: [
CommonModule,
HttpModule,
FormsModule,
ReactiveFormsModule,
RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'counter', component: CounterComponent },
{ path: 'fetch-data', component: FetchDataComponent },
{ path: '**', redirectTo: 'home' }
])
]
})
export class AppModuleShared {
}
Let’s assume that the CounterComponent
is one of the less accessed component. It would be performance worthy to put it under a separate module and load it on demand.
Add a counter.module.ts file under the counter
folder and add the following module code:
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { CounterComponent } from './counter.component';
@NgModule({
imports: [
RouterModule.forChild([{ path: '', component: CounterComponent }])
],
exports: [RouterModule],
declarations: [CounterComponent]
})
export class CounterModule { }
Notice that the module has its own route configuration. Since CounterModule
is a feature module, the forChild
method is used instead of forRoot
for routes configuration.
Modify the app.module.shared.ts and remove the CounterComponet
references from the file:
- Remove the
import
statement
- Remove component declaration from the
declarations
array.
Following is the modified app.module.shared.ts code:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';
import { ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './components/app/app.component';
import { NavMenuComponent } from './components/navmenu/navmenu.component';
import { HomeComponent } from './components/home/home.component';
import { FetchDataComponent } from './components/fetchdata/fetchdata.component';
@NgModule({
declarations: [
AppComponent,
NavMenuComponent,
FetchDataComponent,
HomeComponent
],
imports: [
CommonModule,
HttpModule,
FormsModule,
ReactiveFormsModule,
RouterModule.forRoot([
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'counter', loadChildren:
'./components/counter/counter.module#CounterModule' },
{ path: 'fetch-data', component: FetchDataComponent },
{ path: '**', redirectTo: 'home' }
])
]
})
export class AppModuleShared {
}
Now, we no longer have the component
property; instead we replaced it with loadChilden
. Property loadChildren
takes a relative path to a module that would be lazy-loaded. Notice the module name itself is added at the end of the path string (#CounterModule
). That is because CounterModule
class is not the default
export of the file.
The last piece of the configuration required is in the webpack.configure.js file. But first, we need to install the angular2-router-loader
package.
angular2-router-loader
is a Webpack
loader for Angular that enables string
-based module loading with the Angular Router. Use the following npm install
command to install the package:
npm install --save angular2-router-loader
Of course, change your command prompt’s directory to your application root before running the command.
Moving to configuring the angular2-router-loader
package in the webpack.configure.js. In the use
array, add another entry for angular-router-loader
along with awesome-typescript-loader?silent=true, angular2-template-loader
. The module section should now look like the following:
module: {
rules: [
{ test: /\.ts$/, include: /ClientApp/,
use: ['awesome-typescript-loader?silent=true',
'angular2-template-loader', 'angular2-router-loader'] },
{ test: /\.html$/, use: 'html-loader?minimize=false' },
{ test: /\.css$/, use: ['to-string-loader', 'css-loader'] },
{ test: /\.(png|jpg|jpeg|gif|svg)$/, use: 'url-loader?limit=25000' }
]
}
When finished, build and run the application. To make sure the CounterComponent
is coming from a lazy-loaded module, open the developer
console of your browser and go to the network
tab. Navigating to the counter
route (using the side-menu) will now load a chunk of new code via an HTTP GET
request.