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:
- Preloading strategies
- Performance budgets
- Efficient serving
There are a variety of practices we can apply to shrink our bundles. Two of the most popular ones are:
- Minification and dead code elimination
Code-splitting with Angular
There are two main approaches to code-splitting:
- Component level code-splitting
- 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:
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:
- Generate a new module
- With loadChildren, declare a lazy route in a parent module
- Generate a new component in the lazy loaded module
- 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
The command above will:
- Generate a lazy-loaded module called RankingModule
- Insert a lazy route in app.module.ts
- Generate an eager default route inside the RankingModule
- 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.
There’s a built-in strategy that preloads all the modules in the application. You can use it by configuring the Angular router:
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:
- Quicklink — preload only modules associated with visible links in the viewport
- Predictive prefetching — preload only the modules that are likely to be needed next
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.
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.
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:
- Produce a highly efficient build of your app using the build functionality of the CLI
- 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:
In this post we looked at few practical approaches to speed up an Angular app.
Finally, we talked about efficient serving and the integration of CDN and content compression enabled cloud services with the Angular CLI.