Angular In Depth
Published in

Angular In Depth

Network-aware Preloading Strategy for Angular Lazy Loading

Photo by Mika Baumeister on Unsplash

AngularInDepth is moving away from Medium. More recent articles are hosted on the new platform inDepth.dev. Thanks for being part of indepth movement!

This post explains how to make network-aware preloading strategy for lazy loading of Angular Router. It can improve user experience with lazy loading despite users network condition.

This is a cross-posting of the original post.

What is Preloading?

Preloading is an important feature of Angular Router’s lazy loading. This is available since 2.1.0.

By default, when the application uses lazy loading with loadChildren, chunked lazy modules will be loaded on-demand. It can reduce initial bundle size but users have to wait for loading of chunks on transition.

Preloading changes that. By preloading, the application will start loading chunked modules before needed. It can improve user experience with smooth transition.

Here is the best article to read at first about preloading in Angular by Victor Savkin. He is the author of the feature.

Angular Router: Preloading Modules

Preloading Strategy

Angular Router supports customizing preloading behavior with PreloadingStrategy feature. There are two built-in strategies; PreloadAllModules and NoPreloading.

NoPreloading is the default behavior that doesn't preload any modules.

PreloadAllModules loads all lazy modules immediately after bootstrapping. In other word, this is "As soon as possible" strategy.

import { RouterModule, NoPreloading, PreloadAllModules } from '@angular/router';@NgModule({
imports: [
RouterModule.forRoot(routes, {
preloadingStrategy: PreloadAllModules, // or NoPreloading
}),
],
})
class AppRoutingModule {}

PreloadingStrategy is a simple class object implementing a preload method. So we can make custom preloading strategy in ease like below.

The preload method takes two arguments; route and load . route is a route object that you declare in routes array. load is a function that trigger loading a module.

// custom-preloading-strategy.ts
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, EMPTY } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class CustomPreloadingStrategy implements PreloadingStrategy {
preload(route: Route, load: () => Observable<any>): Observable<any> {
if (shouldPreload(route)) {
return load();
} else {
return EMPTY;
}
}
}
// app-routing.module.ts
@NgModule({
imports: [
RouterModule.forRoot(routes, {
preloadingStrategy: CustomPreloadingStrategy,
}),
],
})
class AppRoutingModule {}

Preloading Problem: Cost of networking

Preloading can improve user experience, but it is only in the case the device uses in fast network enough. Sometimes mobile devices have a narrow-band network connection. If then the application tries to preload all modules ASAP, it affects other connections like AJAX in a bad way.

Preloading is an appropriate solution for users who has a strong network. If they don’t, on-demand loading is better. But this condition can change very dynamically, so the application have to get network information in runtime and turning on/off preloading.

I call that “Network-aware Preloading Strategy”.

Using Network Information API

Network Information API is a new Web standard API proposal. The Network Information API provides information about the system’s connection.

The entire API consists of the addition of the NetworkInformation interface and a single property to the Navigator interface: Navigator.connection . Because this API is not a standard yet, TypeScript doesn't have its type definition. So I've created that as network-information-types package and it is used in all example codes below.

Making Network-aware PreloadingStrategy

Let’s make network-aware preloading strategy with Network Information API! The following code defines shouldPreload function that is used in the above CustomPreloadingStrategy example.

navigator.connection is landed in limited browsers. So we MUST detect the feature. In this case,

export function shouldPreload(route: Route): boolean {
// Get NetworkInformation object
const conn = navigator.connection;
if (conn) {
// With network information
}

return true;
}

Detecting “Save Data” mode

At first, “Save Data” mode should be prioritized the best. It means the user strongly cares about payload size for their cost- or performance-constraints. Use NetworkInformation.saveData property and return false.

export function shouldPreload(route: Route): boolean {
// Get NetworkInformation object
const conn = navigator.connection;
if (conn) {
// Save-Data mode
if (conn.saveData) {
return false;
}

}
return true;
}

Detecting “2G” connection

Network Information API can recognize the network’s effective connection type; 4G, 3G, 2G, and Slow 2G.

In this sample, the application disables preloading when the user is in 2G network.

export function shouldPreload(route: Route): boolean {
// Get NetworkInformation object
const conn = navigator.connection;
if (conn) {
// Save-Data mode
if (conn.saveData) {
return false;
}
// 'slow-2g', '2g', '3g', or '4g'
const effectiveType = conn.effectiveType || '';
// 2G network
if (effectiveType.includes('2g')) {
return false;
}

}
return true;
}

Network Information API has also several other properties like rtt (RTT, round-trip time of the connection). You can add more checks for your application.

Conclusion

  • Angular Router is supporting preloading feature since 2.1.0.
  • You can create your own custom preloading strategy
  • Preloading is effective only for users with a fast network.
  • Network Information API is available in several browsers.
  • It’s very easy to make network-aware preloading strategy.

Thank you for reading!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Suguru Inatomi

Suguru Inatomi

1K Followers

a.k.a. lacolaco / Google Developers Expert for Angular / ng-japan organizer