Menu

Angular Tools for High Performance

August 28th, 2019

This post contains a list of new tools and practices that can help us build faster Angular apps and monitor their performance over time. In each section, you’ll find links for further reference on how to incorporate them in your project. The focus of this post is on decreasing initial load time and speeding up page navigation using code-splitting and preloading.

We’ll look at the following topics:

  • Code-splitting
  • Preloading strategies
  • Performance budgets
  • Efficient serving

JavaScript and initial load time

One of the most expensive types of assets in an app is JavaScript. Once the browser downloads a JavaScript file, it often has to decompress it, after that parse it, and finally execute it. That is why it’s critical for the performance of an app to ship fewer bytes of JavaScript during the initial load time.

There are a variety of practices we can apply to shrink our bundles. Two of the most popular ones are:

  1. Minification and dead code elimination
  2. Code-splitting

The Angular CLI has been doing a great job minifying bundles and eliminating dead code. In version 8, the CLI also introduced differential loading support, which can reduce the amount of JavaScript for modern browsers even further. All this is completely automated by the tooling Angular provides.

On the other hand, code-splitting is entirely in our hands. The next section is dedicated on how to shrink our JavaScript bundles by using this technique.

Code-splitting with Angular

There are two main approaches to code-splitting:

  1. Component level code-splitting
  2. Route level code-splitting

The main difference between these two techniques is that with component level code-splitting, we can load individual components lazily even without route navigation. For example, we can load the component associated with a chatbox only after the user clicks on a placeholder.

With route level code-splitting, we load the individual routes lazily. For example, if the user is in the home page of an app and they navigate to the settings page, Angular will first download the corresponding bundle and after that render the route.

Component level code-splitting

Component level code-splitting has been hard in Angular because of the factories that the current version of the Angular compiler generates. The good news is that Ivy will enable a simpler mechanism for it. In the future releases of the framework, we’ll work on using these capabilities to deliver ergonomic APIs for component level code-splitting. Until then, there are two community libraries you can use to achieve ergonomic code-splitting on a component level:

  1. ngx-loadable
  2. @herodevs/hero-loader

Route-level code-splitting

Now let’s focus on route-level code-splitting. You can learn more about it here. This technique involves boilerplate code. To create a lazy route manually, we need to:

  1. Generate a new module
  2. With loadChildren, declare a lazy route in a parent module
  3. Generate a new component in the lazy loaded module
  4. Declare an eager route declaration in the lazy module

With Angular CLI version 8.1, you can now achieve this with a single command! To generate a lazy module use: https://medium.com/media/4a43c769adee95d68aee89a1dcb50741/href

For example:
https://medium.com/media/c9f2427d0f1234dd9586ad7de1f5d47e/href

The command above will:

  1. Generate a lazy-loaded module called RankingModule
  2. Insert a lazy route in app.module.ts
  3. Generate an eager default route inside the RankingModule
  4. Generate a component that will handle the eager default route

Once we introduce a lazy route in our app, when the user navigates to it, Angular will first download the corresponding bundle from the network. On a slow internet connection, this could lead to a bad UX.

To work around that, Angular provides router’s preloading strategy.

Preloading Modules

There’s a built-in strategy that preloads all the modules in the application. You can use it by configuring the Angular router:
https://medium.com/media/a706d8890575976c30327ba930b64957/href

Angular Tools for High Performance
Preloading of JavaScript

The risk with this strategy is that in an application with many modules, it may increase the network consumption and also block the main thread when Angular registers the routes of the preloaded modules.

For larger apps, we can apply more advanced preloading heuristics:

  1. Quicklink — preload only modules associated with visible links in the viewport
  2. Predictive prefetching — preload only the modules that are likely to be needed next

The Angular implementation of the quicklink preloading strategy is ngx-quicklink. You can find a detailed guide on how to use it here.

Predictive prefetching uses a report for your app’s usage. You can find how to introduce predictive prefetching with Guess.js to your Angular CLI app in the video below:

Code-splitting can greatly improve the performance of our apps but it doesn’t provide us with any guarantees that it won’t regress over time. For this purpose, we can use performance budgets.

Performance budgets

To monitor our apps over time, the Angular CLI supports performance budgets. The budgets allow us to specify limits in which the production bundles of our app can grow. In the workspace configuration, under the budgets section, we can specify several different types of budgets:

All of them accept maximumWarning and maximumError. If we exceed the budget’s maximumWarning value, the CLI will show a warning. If we exceed the maximumError value, the build will fail.

Here’s a short video which shows how to quickly setup budgets for your project:

It’s very convenient to introduce budgets as part of your CI so that you can keep track of them across code changes. Read more about this here.

Efficient serving

Looking at datasets of a lot of Angular apps running into the wild, we noticed that over 25% of them do not use content compression. Even more, don’t use a Content Delivery Network (CDN). These are two easy wins that are very simple to implement as part of the deployment pipeline.

To allow developers to ship fast Angular apps from end-to-end as part of Angular CLI version 8.3 we’ll introduce a new command called deploy. By simply running ng deploy, you’ll be able to:

  1. Produce a highly efficient build of your app using the build functionality of the CLI
  2. Deploy your app to a selected hosting provider

We’ve been working very closely with Google Cloud & Firebase, Azure, and Zeit to introduce deployment capabilities for their platforms directly from the CLI. There are also third-party packages for Netlify and GitHub pages.

The list of packages available at the moment of writing of this article are:

  • @angular/fire
  • @azure/ng-deploy
  • @zeit/ng-deploy
  • @netlify-builder/deploy
  • angular-cli-ghpages

Conclusion

In this post we looked at few practical approaches to speed up an Angular app.

We saw how to reduce the size of our JavaScript bundles using component-level and route-level code-splitting. As the next step, we discussed preloading strategies that can speed up page navigation. To monitor our app’s performance over time we introduced performance budgets.

Finally, we talked about efficient serving and the integration of CDN and content compression enabled cloud services with the Angular CLI.