Preloading
Strategies for Angular Modules
Angular RTP Meetup
Hey, Am Udhay (OO-dhy)
Agenda
- Single Module Angular App
- Multi Module Angular App
- Lazy loading
- Preloading Modules:
- All
- Selective
- Network based
- Quick link
- Discussion (Q/A)
Single Module Angular App
- App contains only one module that has Components, Pipes, Directives and Services.
- It works great for small web application with five to ten pages.
Module
Components
Pipes
Directives
Multi Module Angular App
- App contains more than one module with App module being the root module. Each modules contains Components, Pipes, Directives and Services.
- It works great for Enterprise level applications
Root / Main
Home
Products
Cart
Payment
Orders
Lazy loading
Lazy loading
- Loads Angular Module only when the route is visited / accessed.
Root / Main
Home
Products
Cart
Payment
Orders
/home
/products
/cart
/billing
/orders
https://someonlinestore.com/
Lazy loading (Contd)
Create new Angular project
ng new someonlinestore --routing
ng generate module payment --route billing --module app.module
Create feature module (Angular takes care of lazy loading configuraiton)
Lazy loading (Contd)
Application's Root Module Routes look like:
const routes: Routes = [
{
path: 'billing',
loadChildren: () =>
import('./payment/payment.module').
then((m) => m.PaymentModule),
}
];
When User accesses https://someonlinestore.com/billing, Payment module gets loaded and their content painted on UI
Drawback of Lazy Loading
- If the bundle (Feature module) size is huge, it makes User to wait couple moments to load the page when the respective route is accessed.
This can be fixed by Preloading the module that's huge in size based on different strategies. Let's dig into it more..
Preloading Strategy - All
Preload All Modules by setting preloadingStrategy router option as below:
@NgModule({
imports: [
RouterModule.forRoot(routes,
{ preloadingStrategy: PreloadAllModules }
),
],
exports: [RouterModule],
})
Preloading Strategy - Selective
Set Preload JSON parameter value to either true or false in the route. And update preloadingStrategy to SelectivePreloadStrategy (let's create it).
const routes: Routes = [
{
path: 'home',
loadChildren: () => import('./home/home.module').then((m) => m.HomeModule),
data: {preload: true}
},
{
path: 'about',
loadChildren: () =>
import('./about/about.module').then((m) => m.AboutModule),
data: {preload: false}
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {preloadingStrategy: SelectivePreloadStrategy}),
],
exports: [RouterModule],
})
Preloading Strategy - Selective (Contd)
Create a Service class 'SelectivePreloadStrategy' & implement PreloadingStrategy Interface. Add logic within preload method that decides whether to preload module or not..
import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, EMPTY } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class SelectivePreloadStrategy implements PreloadingStrategy{
constructor() { }
preload(route: Route, load: () => Observable<any>): Observable<any>{
return route.data && route.data['preload'] ? load() : EMPTY;
}
}
const routes: Routes = [
{
path: 'home',
loadChildren: () => import('./home/home.module').then((m) => m.HomeModule),
data: {preload: true}
},
{
path: 'about',
loadChildren: () =>
import('./about/about.module').then((m) => m.AboutModule),
data: {preload: false}
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {preloadingStrategy: NetworkPreloadStrategy}),
],
exports: [RouterModule],
})
Preloading Strategy - Network Connection Based
Update preloadingStrategy to NetworkPreloadStrategy (let's create it).
import { Injectable } from '@angular/core';
import { PreloadingStrategy } from '@angular/router';
import { EMPTY, TimeoutError } from 'rxjs';
export declare var navigator;
@Injectable({
providedIn: 'root'
})
export class NetworkPreloadStrategy implements PreloadingStrategy{
constructor() { }
preload(route: import("@angular/router").Route, load: () => import("rxjs").Observable<any>): import("rxjs").Observable<any> {
return this.hasGoodNetworkConn() ? load() : EMPTY;
}
hasGoodNetworkConn() {
const conn = navigator.connection;
if(conn){
const connectionsToAvoid = ['2g', '3g'];
const connectionType = conn.effectiveType || '';
console.log(connectionType);
if(connectionsToAvoid.includes(connectionType)){
return false;
}
}
return true;
}
}
Preloading Strategy - Network Connection Based (Contd)
Decide preloading based on Network..
Preloading Strategy - Quick Links Strategy
const routes: Routes = [
{
path: 'home',
loadChildren: () => import('./home/home.module').then((m) => m.HomeModule),
data: {preload: true}
},
{
path: 'about',
loadChildren: () =>
import('./about/about.module').then((m) => m.AboutModule),
data: {preload: false}
},
];
@NgModule({
imports: [
RouterModule.forRoot(routes, {preloadingStrategy: QuickLinkStrategy}),
],
exports: [RouterModule],
})
Using ngx-quicklink, the configuration is straightforward..
Import the QuicklinkModule in AppModule and add QuickLinkStrategy as Preloading strategy
Links
Twitter- https://twitter.com/AskUdhay
Check my previous talks - http://askudhay.com/