Explain Angular router preloading strategies.
Angular's router supports lazy loading of modules, which means parts of your application are loaded only when they are needed. While lazy loading improves initial load times, subsequent navigation to a lazy-loaded route still involves a network request. Preloading strategies address this by loading lazy modules in the background after the initial application bootstrap, improving the user experience for subsequent navigations.
What is Preloading?
Preloading is a technique where lazy-loaded Angular modules are fetched from the server and processed by the browser in the background, shortly after the application's initial load. This happens before the user actually navigates to those routes, making subsequent navigations instantaneous as the modules are already available in the browser's memory.
Why Use Preloading?
- Improves user experience by making subsequent navigations feel faster and more seamless.
- Reduces perceived loading times for lazy-loaded sections of the application.
- Optimizes resource utilization by loading modules during idle times.
Configuring Preloading
Preloading strategies are configured in the root module's RouterModule.forRoot() function.
import { RouterModule, Routes, PreloadAllModules } from '@angular/router';
const routes: Routes = [
// ... your routes
];
@NgModule({
imports: [RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })],
exports: [RouterModule]
})
export class AppRoutingModule { }
Angular's Built-in Preloading Strategies
1. NoPreloading
This is the default strategy. It disables preloading for all lazy-loaded modules. Modules are loaded only when the user navigates to their routes.
RouterModule.forRoot(routes, { preloadingStrategy: NoPreloading })
2. PreloadAllModules
This strategy preloads all lazy-loaded modules immediately after the application has finished loading and bootstrapping. It ensures that all modules are ready when the user navigates to them, but it can consume more bandwidth and potentially delay initial app interactivity if too many modules are preloaded.
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
Custom Preloading Strategies
For more fine-grained control, you can create a custom preloading strategy by implementing the PreloadingStrategy interface. This allows you to define your own logic for which modules to preload and when.
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';
export class CustomPreloadingStrategy implements PreloadingStrategy {
preload(route: Route, fn: () => Observable<any>): Observable<any> {
// Your custom logic here
if (route.data && route.data['preload']) {
return fn(); // Preload this module
} else {
return of(null); // Do not preload this module
}
}
}
The preload method receives two arguments:
* route: The route configuration object.
* fn: A function that returns an Observable. If you want to preload the module associated with the route, you call and return the result of fn(). If not, you return of(null).
Example: Selective Preloading Strategy
A common custom strategy is to preload only specific modules. You can mark routes in their configuration with a custom data property.
const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),
data: { preload: true } // Mark for preloading
},
{
path: 'reports',
loadChildren: () => import('./reports/reports.module').then(m => m.ReportsModule)
// No 'preload' data, so it won't be preloaded by this strategy
}
];
Then, your custom preloading strategy would check this data property:
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';
export class SelectivePreloadingStrategy implements PreloadingStrategy {
preload(route: Route, fn: () => Observable<any>): Observable<any> {
if (route.data && route.data['preload']) {
console.log('Preloading: ' + route.path);
return fn();
}
return of(null);
}
}
Finally, you provide and use your custom strategy:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { SelectivePreloadingStrategy } from './selective-preloading-strategy';
// ... routes definition
@NgModule({
imports: [RouterModule.forRoot(routes, { preloadingStrategy: SelectivePreloadingStrategy })],
exports: [RouterModule],
providers: [SelectivePreloadingStrategy] // Provide your custom strategy
})
export class AppRoutingModule { }
Other Custom Preloading Scenarios
- Network-Aware Preloading: Check network conditions (e.g., using
navigator.connection.effectiveType) and preload only if the connection is fast enough. - User-Idle Preloading: Use a library like
ngx-web-workeror custom logic to preload modules only when the user's browser is idle. - Preloading by User Role/Permissions: Preload modules relevant to the logged-in user's role.
- Predictive Preloading: Based on user behavior analytics, predict which modules the user is likely to visit next and preload those.
Choosing the Right Strategy
| Strategy | Description | Use Case |
|---|---|---|
| NoPreloading | Modules are loaded only when a user navigates to their routes (on-demand). | Small applications, applications with very specific or rarely visited sections, or when initial bundle size and bandwidth are critical constraints. |
| PreloadAllModules | All lazy-loaded modules are preloaded immediately after the application starts. | Applications where most users will likely visit all sections, applications with good internet connectivity, or when the goal is the fastest possible subsequent navigation. |
| Custom Strategy | Allows defining custom logic to decide which modules to preload and when. | Complex applications, scenarios requiring optimized resource usage based on user behavior, network conditions, user roles, or specific business rules. (e.g., Selective Preloading, Network-Aware Preloading). |