Introduction
By default, angular modules are eagerly loaded, which means that all feature modules are loaded as soon as the application loads whether or not they are immediately necessary.
Why
For large applications with lots of routes, user might not visit all of them frequently. So loading required modules on demand helps keep initial bundle sizes smaller, which in turn helps decrease load times.
How it is with eager loading
Assuming you have an application with a feature module(Itemsmodule
). This is how your application routes looks like with an Eager loading design pattern.
If you look at below code, its basically telling Angular to load ItemComponent
on the screen whenever user navigates to /items
const routes: Routes = [
{ path: 'items', component: ItemComponent }
];
And you would have ItemsModule
under imports of AppModule
app.module.ts
const routes: Routes = [
{ path: 'items', component: ItemComponent }
];
@NgModule({
declarations: [AppComponent],
imports: [
ItemsModule,
RouterModule.forRoot(routes)
]
})
export class AppModule {
constructor() {}
}
With this, ItemsModule
will be loaded as soon as the application loads.
How it would be with lazy loading
Now lets look how this can be done with lazy loading design pattern.
To lazy load Angular modules, use loadChildren
instead of component
in your routes
configuration as shown below.
Notice that the lazy-loading syntax uses
loadChildren
followed by a function that uses the browser's built-inimport('...')
syntax for dynamic imports. The import path is the relative path to the module.
const routes: Routes = [
{
path: 'items',
loadChildren: () => import('./items/items.module').then(m => m.ItemsModule)
}
];
And you would then remove itemsModule
from app.module.ts
as we no longer loading it on initial load.
app.module.ts
const routes: Routes = [
{
path: 'items',
loadChildren: () => import('./items/items.module').then(m => m.ItemsModule)
}
];
@NgModule({
declarations: [AppComponent],
imports: [
RouterModule.forRoot(routes)
]
})
export class AppModule {
constructor() {}
}
Looks confusing? it does at first, but all its telling to Angular is, "whenever user visits /items
only then load itemsModule
"
Now, there is one more step to understand, so far we have only loaded itemsModule
at app.module.ts
level. We are yet to mention which component in itemsModule
has to be loaded when user visits /items
.
To do that, we have to go inside the feature module(itemsModule
) to mention which component to load.
items.module.ts
const routes: Routes = [
{
path: '',
component: ItemComponent
}
];
@NgModule({
imports: [ RouterModule.forChild(routes) ]
})
export class ItemsModule {
constructor() {}
}
The path
here is set to an empty string because the path in AppRoutingModule
is already set to /items
.
Every route in this feature module is a child route.
So if you need something like /items/frequent
, you just have to define that child route inside feature module like shown below.
items.module.ts
const routes: Routes = [
{
path: '',
component: ItemComponent
},
{
path: '/frequent',
component: FrequentComponent
}
];
@NgModule({
imports: [ RouterModule.forChild(routes) ]
})
export class ItemsModule {
constructor() {}
}
So now, /items
takes user to ItemComponent
and /items/frequent
takes user to FrequentComponent
yay!!! Now you understood Lazy loading design pattern. Angular docs have an excellent detailed explanation on this topic. I would recommend to read it.
For a sample application with two lazy-loaded modules see the live example taken straight out of Angular docs.
Top comments (3)
Nice article Jag👍
Nice article, thank you for writing it.
Shouldn't ItemsModule use RouterModule.forChild? Otherwise, non-tree-shakable router providers such as the Router service will have duplicate instances.
Correct. Its my mistake while editing post. It should be forChild. Thanks for pointing. I have corrected it.