9.6.2021

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.

Sven

Softwareentwickler

Zur Übersicht

Standort Hannover

newcubator GmbH
Bödekerstraße 22
30161 Hannover

Standort Dortmund

newcubator GmbH
Westenhellweg 85-89
44137 Dortmund