Lazy Loading in Angular
Amazon found out that every 100ms of Latency cost them 1% in Sales
Latency starts when entering a website. To minimize latency on startup it is important to keep the initial bundles as small as possible. Angular's build system already does a good job in minimizing your code. However, you as a developer can make the bundles even smaller by leveraging Angular's lazy loading features. Out of the box it provides three mechanisms to reduce the initial bundle size:
- route based code splitting
- lazy components
- bundle size budgets
Route based code splitting
The idea of route based code splitting is pretty simple. If you do not see a page why should the client even bother loading it? That's why Angular's router has built in route based code splitting also called lazy loading. A lazy route is defined using the loadChildren
property of a route config:
{
path: '',
pathMatch: 'full',
component: MainPageComponent,
},
{
path: 'map',
loadChildren: () => import('./map-module/map.module').then(m => m.MapModule),
}
In this example the MainPageComponent
is loaded eagerly thus is included in the initial bundle. The map route on the other hand is defined as a lazy route using loadChildren
and an ES feature called dynamic imports
. It is important to note that the MapModule
is referenced as a string. That way we do not import that module directly and the code splitting tool does not spot any connection to code in the initial bundle and can safely put it in a dedicated bundle.
Therefor, if you import any single line of code from that lazy module in another module you break the code splitting and your initial bundle size may explode.
Lazy loading single components
The dynamic imports feature can also be used to lazy load single components. It should be used if you have rather heavy components such as maps or charts.
import('../../core-module/components/chart-tab/chart-tab.component').then(({ ChartTabComponent }) => {
const chartTabFactory = this.cfr.resolveComponentFactory(ChartTabComponent);
const { instance } = this.chartContainer.createComponent(chartTabFactory, null, this.injector);
});
This example lazily loads a ChartTabComponent via string reference and then uses Angular's ComponentFactoryResolver
to instantiate the lazy component and to render it into dedicated html container element. This code is rather short and can be placed in the initial bundle which is first executed when the initial page load is done resulting in the page around the chart being visible and usable by the user much earlier.
Bundle size budgets
Angular's build system offers you to check the bundle sizes of your code against a configurable threshold. Just add the following config to your angular.json
file:
"budgets": [
{
"type": "initial",
"maximumWarning": "2mb",
"maximumError": "5mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
]
In this example there will be a warning during build time when the initial bundle size is larger than 2mb or even a build failure if the size is larger than 5mb. It is even possible to check the stylesheet size on a component level which in this case is limited to 6kb.
Budgets are a powerful tool to keep the bundles small. If you unintentionally import a large library or a lazy loaded route/module/component you will notice it right away in your build step.
Standort Hannover
newcubator GmbH
Bödekerstraße 22
30161 Hannover
Standort Dortmund
newcubator GmbH
Westenhellweg 85-89
44137 Dortmund