Lazy loading is a design pattern in Angular that allows modules to be loaded on demand rather than at the application startup. This approach reduces the initial bundle size, improves application performance, and optimizes resource usage for large applications. In this post, we will go through step-by-step instructions for setting up lazy loading in Angular, including code examples and best practices.
1. Introduction to Lazy Loading
Angular applications often consist of multiple feature modules. By default, all modules are loaded when the application starts, which can increase the initial load time. Lazy loading allows developers to load feature modules only when the user navigates to a route associated with that module.
Benefits of lazy loading:
- Faster initial load times.
- Improved performance for large applications.
- Efficient use of bandwidth.
- Better modularity and separation of concerns.
Lazy loading is achieved in Angular using the loadChildren
property in routing configuration.
2. Prerequisites
Before implementing lazy loading, make sure:
- Angular CLI is installed.
- Your application is structured into feature modules.
- You are familiar with Angular routing and
NgModule
.
3. Step 1: Create a Feature Module with Routing
To implement lazy loading, start by creating a feature module. You can do this using the Angular CLI:
ng generate module admin --route admin --module app.module
Explanation:
admin
is the feature module.--route admin
tells Angular to automatically set up routing for this module.--module app.module
registers the module in the main application routing.
Alternatively, you can manually create a module and routing module:
// admin.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdminComponent } from './admin.component';
import { AdminRoutingModule } from './admin-routing.module';
@NgModule({
declarations: [AdminComponent],
imports: [CommonModule, AdminRoutingModule]
})
export class AdminModule {}
// admin-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminComponent } from './admin.component';
const routes: Routes = [
{ path: '', component: AdminComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdminRoutingModule {}
Here, AdminModule
is a feature module that contains its own routing.
4. Step 2: Remove Feature Module from AppModule Imports
If you previously imported the AdminModule
in AppModule
, remove it. Importing it in AppModule
would bundle it into the main application, defeating the purpose of lazy loading.
// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
AppRoutingModule, // Feature modules will be lazy loaded here
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
5. Step 3: Configure Lazy Loading in Main Routing File
In the main routing module (app-routing.module.ts
), configure lazy loading using loadChildren
:
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
},
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: '**', redirectTo: 'home' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Explanation:
- The
loadChildren
function dynamically imports the module only when the user navigates to/admin
. - This ensures the module is not included in the initial bundle.
- Wildcard and redirect routes handle invalid URLs and default paths.
6. How Angular Loads Modules Lazily
When a user navigates to /admin
:
- Angular fetches the module’s JavaScript bundle.
- The module is instantiated.
- Angular renders the associated component(s).
Until the user navigates, the module code does not exist in memory, saving resources.
7. Nested Lazy Loading
Angular allows nested lazy-loaded modules. For example, you could have an AdminSettingsModule
loaded only when /admin/settings
is visited:
// admin-routing.module.ts
const routes: Routes = [
{ path: '', component: AdminComponent },
{
path: 'settings',
loadChildren: () => import('./settings/settings.module').then(m => m.SettingsModule)
}
];
8. Preloading Strategies
Lazy loading reduces initial load time, but sometimes it’s beneficial to preload certain modules after the application starts. Angular provides PreloadAllModules
strategy:
import { NgModule } from '@angular/core';
import { RouterModule, Routes, PreloadAllModules } from '@angular/router';
const routes: Routes = [
{ path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) }
];
@NgModule({
imports: [RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })],
exports: [RouterModule]
})
export class AppRoutingModule {}
This approach lazy loads modules but preloads them in the background, improving navigation performance.
9. Route Guards with Lazy Loading
You can protect lazy-loaded modules using route guards such as CanLoad
:
// auth.guard.ts
import { Injectable } from '@angular/core';
import { CanLoad, Route, UrlSegment, Router } from '@angular/router';
@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanLoad {
canLoad(route: Route, segments: UrlSegment[]): boolean {
const isAuthenticated = !!localStorage.getItem('token');
if (!isAuthenticated) {
this.router.navigate(['/login']);
}
return isAuthenticated;
}
constructor(private router: Router) {}
}
Apply the guard to the lazy-loaded route:
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
canLoad: [AuthGuard]
}
10. Lazy Loading Best Practices
- Modularize Features: Create separate modules for each feature.
- Use Routing: Always combine feature modules with routing.
- Avoid Eager Imports: Do not import lazy modules in
AppModule
. - Guard Sensitive Routes: Use
CanLoad
orCanActivate
for restricted access. - Preload When Needed: Use
PreloadAllModules
for critical features. - Optimize Bundle Size: Keep modules small to maximize the benefit of lazy loading.
11. Debugging Lazy Loading
- Check Network Tab: Ensure the feature module is only loaded on route access.
- Look for Errors: Missing
RouterModule.forChild
or wrong paths can cause errors. - Verify Guards: Ensure
CanLoad
does not block legitimate access unintentionally.
12. Summary
Lazy loading is essential for large Angular applications to improve performance and user experience. Steps:
- Create a feature module with its own routing.
- Remove the module from AppModule imports.
- Configure lazy loading in
AppRoutingModule
usingloadChildren
.
Optional improvements:
- Use nested lazy loading for sub-features.
- Implement preloading strategies.
- Protect modules with route guards.
By following these practices, Angular applications can scale efficiently without compromising on performance.
Example Complete Code
// admin.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AdminComponent } from './admin.component';
import { AdminRoutingModule } from './admin-routing.module';
@NgModule({
declarations: [AdminComponent],
imports: [CommonModule, AdminRoutingModule]
})
export class AdminModule {}
// admin-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AdminComponent } from './admin.component';
const routes: Routes = [
{ path: '', component: AdminComponent }
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class AdminRoutingModule {}
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes, PreloadAllModules } from '@angular/router';
const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)
},
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: '**', redirectTo: 'home' }
];
@NgModule({
imports: [RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })],
exports: [RouterModule]
})
export class AppRoutingModule {}
This setup ensures AdminModule
is loaded only when required, optimizing your Angular application.
Leave a Reply