Bundling and Code Splitting in Angular
Sep 10, 2020 • 7 Minute Read
Introduction
As personal computers and mobile phones have gained more and more processing power, we have seen the rise of the client-side web app. When writing a client-side app using Angular, it is important that you consider the size of the overall JavaScript bundle that you are shipping to production. All too often, bloated web apps are being shipped that contain much more JavaScript than they need to. Too much JavaScript can slow down your app considerably—especially in terms of the initial loading speed of your app!
In this guide, you will learn how to configure bundling in your Angular app so that you can stay on top of the size of your bundle at all times. After that, you will learn one of the best strategies for dealing with a large app: code splitting, or lazy loading. You will learn how to lazy load modules and components in your app so your user only downloads the JavaScript they need to.
Let's get started.
Configuring Your Bundle Using the Angular CLI
To understand how bundling works in an Angular app, you don't need to look further than the angular.json file. The angular.json file that is generated for your Angular app is the configuration file that is used by the underlying build system contained within the Angular CLI. The angular.json has a lot of different sections, but in this guide, we will focus on the JavaScript bundling configuration options it provides.
The first step to configuring your production bundle is to set build size warnings. This can be accomplished using the budgets field within the angular.json. The budgets field is located on the production configuration portion of the JSON file. Below, you will find the default production configuration for an Angular app. The following JSON sets an initial sizing budget for the app and a budget for any of the component style files.
....
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb",
"maximumError": "10kb"
}
]
....
This configuration will relay a build warning if your final bundle size reaches 2 megabytes and a build error if your bundle size reaches 5 megabytes. It will do the same thing for the individual size of any component style file, but for 6 kilobytes and 10 kilobytes respectively. This is a very powerful technique that ensures you can configure sizing limits based on the needs of your particular team or app. There are many different types of budgets that can be used. For a full listing, check out the budgeting portion of the Angular documentation.
Apart from providing an easy-to-use API for helping you stay on top of your bundle size, the angular.json file also provides an easy way for you to include third-party scripts into your bundle. This can easily be achieved by using the scripts field located on your project's build options configuration. Below, you will find an example of this field that pulls in an external JavaScript file. This file will be included inside the final production bundle.
...
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
...
"scripts": ['third-party-deps/external-script.js']
},
...
The angular.json file makes it very easy to include external scripts within your bundle. This can be a powerful mechanism to help organize and unify the flow of third party dependencies if you have a very customized build environment.
Code-Splitting
So you've learned how to add scripts to your bundle and how to configure warnings and errors to your build in order to help keep your bundle small. But what if your bundle is necessarily large? In this section, you will learn how to take advantage of Angular's code-splitting (lazy-loading) API in order to successfully split your bundle up into smaller, more performant chunks.
Code splitting is achieved in Angular by creating lazy-loaded modules. In order to ensure that a module is lazy loaded and split into its own chunk of JavaScript, you must first create a lazy-loaded route. The following code demonstrates a lazy-loaded route in Angular.
const routes: Routes = [
{
path: 'user-settings',
loadChildren: () => import('./user-settings/user-settings.module').then(m => m.UserSettingsModule)
}
];
The first thing this code does is tell the Angular CLI to split this module into its own bundle. For example, if the above route is the only lazy-loaded module in your app, then when you build your app, you will get another, smaller chunk of JavaScript created alongside your main bundle. Your main bundle will be smaller as this module is chunked into its own file. Apart from this, the above code tells the Angular RouterModule to only load the UserSettingsModule when the user navigates to the user-settings path.
This is a powerful means of chunking up your production bundle and ensuring that your initial time to paint is fast. Lazy loading ensures that users do not download all of the JavaScript used by your app at once but only on demand.
Note: In the above example, you would need to ensure that the UserSettingsModule is not imported into the root module of your application any longer. Continue to follow routing best practices! For more info, see the Angular lazy-loaded routes documentation.
Conclusion
In this guide, you learned all about bundling when it comes to your Angular app. You gained an understanding of how to use the configurative power of the angular.json file to configure your bundle. You learned how to include third party JavaScript files directly and how to set warnings related to your bundle size so that you can stay in the know when it comes to your bundle. You also learned the primary strategy for dealing with necessarily large bundle sizes, i.e., lazy loading. You learned how to split your bundle up into bite-size chunks that can then be lazily loaded into your app and how using this technique can make your app much faster and more easily consumable by your userbase.
I hope this guide has helped you gain a deeper understanding of bundling and code splitting in your Angular app. For more information regarding lazy loading, check out the relevant Angular documentation.