Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Optimizing App Loading Time With Angular 2 Lazy Loading

DZone's Guide to

Optimizing App Loading Time With Angular 2 Lazy Loading

Developing mobile apps with Angular 2 may result in a file that's too large. With lazy loading, we can split our application to feature modules and load them on demand.

· Performance Zone ·
Free Resource

Sensu is an open source monitoring event pipeline. Try it today.

When you develop mobile applications, you should always be alert on the performance and always optimize. In this blog post, I will walk you through a very effective pattern that describes optimizing the loading time of an application when using Angular. Meet Angular lazy loading.

Developing mobile apps with Angular 2 may result in an application file that is too large and affects the startup time. Luckily, the Angular router provides a great feature called lazy loading to help us decrease the initial loading time.

What Is Lazy Loading?

With lazy loading, we can split our application to feature modules and load them on demand. The main benefit is that, initially, we can only load what the user expects to see on the start screen. The rest of the modules are only loaded when the user navigates to their routes.

Check out this blog post for more in-depth information about lazy loading.

Using Lazy Loading With NativeScript

The Angular 2 built-in module loader uses SystemJS. However, when developing NativeScript apps, we don't have to set up SystemJS to create lazy loaded modules. We can provide a custom module loader or not use a module loader for lazy loading at all.

In the following sections, we will use the simple lazyNinjas app which has two modules: the HomeModule (not lazy loaded) and the NinjasModule (lazy loaded). It also has two branches: callback-loading and custom-module-loader. We'll cover the two strategies in the next two sections. Take a minute to make yourself familiar with the ninjas; download the app from GitHub and run it.

Providing a Callback to the loadChildren Property

Let's take a look at our router configuration:

The routes array is our app's actual router configuration. First, we simply add the HomeModule routes using the spread operator .... Then we register the lazy loaded NinjasModule. The interesting part is the value we pass to the loadChildren` property:

loadChildren: () => require("./ninjas/ninjas.module.js")["NinjasModule"]

Here we pass a callback with a single return statement. Let's see what happens step by step.

First of all, in the file ./ninjas/ninjas.module.ts, we write our NinjasModule along with its routes and import them using the forChild method of  NativeScriptRouterModule. Here's how it looks like:

Note that the exported object has a single element called NinjasModule. That allows it to require the module object as we do in the callback, passed to the loadChildren property.

loadChildren: () => require("./ninjas/ninjas.module.js")["NinjasModule"]

We can also omit the extension of the file, and write the above as:

loadChildren: () => require("./ninjas/ninjas.module")["NinjasModule"]


You can find that version of the app in the callback-loading branch.

Providing a Custom Module Loader

Instead of passing a callback to every loadChildren property, we can extract the loading logic to a module loader. Then, we can use it instead of the built-in loader that uses SystemJS.

Let's take a look at the NinjaModuleLoader used in our simple app:

The loader implements the NgModuleFactoryLoader, which has a single method — load, with one parameter — the path, which we'll pass to the loadChildren property. We'll adopt the same syntax that the built-in module loader uses. As stated in the Angular documentation:

"Lazy loaded module location is a string, not a type... the string identifies both the module file and the module class, the latter separated from the former by a #."

We should change all loadChildren properties in our router configuration. Luckily, we have only one:

loadChildren: "./ninjas/ninjas.module#NinjasModule"


The private splitPath method of the NinjasModuleLoader is where we extract the module file location and the exported module name. Then we can simply require the module in the same way as in our callback function:

let loadedModule = require(modulePath)[exportName];


Extracting the loading logic allows us to check if the module exists and throw a meaningful error message if it doesn't.

if (!loadedModule) {   throw new Error(`Cannot find "${exportName}" in "${modulePath}"`);}

If the module was successfully required, we compile asynchronously it with the Angular compiler:

return this.compiler.compileModuleAsync(loadedModule);


Finally, we need to register our module loader in the root module (Note that you can have a different module loader for every NgModule!).

You can find the final version of the app in the custom-module-loader branch.

Benefits From Lazy Loading

For a real-life NativeScript app with lazy loaded modules, you can check out our SDK Examples. It has more than 100 different components, each with its own route, and ~15 feature modules. The following table presents the startup times with and without lazy loading.

Platform Device No lazy loading With lazy loading
Android Nexus 5 ~13s ~4s
iOS iPhone 5S ~12s ~3s

For our 2.5 release, we are working to enable ahead of time compilation and Webpack 2 with treeshaking and obfuscation, which will also improve the loading time significantly. Expect an update on this in the following weeks.

Sensu: workflow automation for monitoring. Learn more—download the whitepaper.

Topics:
performance ,lazy loading ,app development ,angular 2 ,optimization

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}