Skip to content

Optimizing Your Angular Apps

March 5, 2022


Angular is huge, don't make it worse.


Optimizing your applications is critical for gaining and keeping your users.

https://wpostats.com/

I recommend the following for keeping your angular apps in check:

  • Analyze

  • Code Split

  • Tree Shake

  • Configure

1. Analyze

Before starting to address any issues with the size of your app bundles, analyze your production builds and pinpoint where to spend your effort. Aim for the lowest hanging fruits first, and then repeat the "optimization cycle" until your goals are met.

A note on deminishing returns: it is important to be pragmatic, as shedding size off of an existing application can be time consuming and difficult to really make progress after all the easy misses are resolved. That is OK. As long as you are avoiding major pitfalls, you are doing good.

Run your build with --sourceMap=true to generate the appropriate data for analysis.

npm run build -- --configuration=pipeline --sourceMap=true

You can then analyze your bundles using source-map-explorer

Windows:

npx source-map-explorer .\dist\my-app\main.*.js

Mac:

npx source-map-explorer dist/my-app/main.*.js

Note: Webpack bundle analyzer is no longer the recommended way to analyze your angular bundles. Instead, source-map-explorer provides more accurate analysis. https://angular.io/guide/deployment#inspect-the-bundles

2. Code Split

Code split using routes

Angular allows applications to be code split by their modules. For example a URL such as /dashboard may be contained within DashboardModule and /admin may be contained within AdminModule. By configuring these components properly using angular's recommended mechanism, the compiler will create separate bundles, splitting your code apart.

const routes: Routes = [
  {
    path: "items",
    loadChildren: () =>
      import("./items/items.module").then((m) => m.ItemsModule),
  },
];

https://angular.io/guide/lazy-loading-ngmodules

Tips

Do not use shared modules. Always use a separate module per route section, and allow the compiler to generate more granular chunks.

A future angular release will make this easier and lighter-weight:

3. Tree-Shake unused dependencies from your bundles

Proper imports can improve code splitting. Improper imports can bloat your bundles.

For instance, while it can be convenvient to expose exports as an object, compilers such as webpack cannot properly tree-shake unused values from that shared model.

  • [Bad]

    import { ClientModel } from "./backend/models";
    import { ClientModel } from "./backend/models/index.ts";
    
  • [Good]

    import { ClientModel } from "./backend/models/client";
    import { ClientModel } from "./backend/models/client.ts";
    

Tips

Do not export objects or classes if you can help it. Instead, export simple variables and standalone functions. This way, the compiler can properly detect used vs unused code.

4. Configure

Server and build configurations can make all the difference. These include:

Enable HTTP/2

HTTP/2 will make our applications faster, simpler, and more robust — a rare combination — by allowing us to undo many of the HTTP/1.1 workarounds previously done within our applications and address these concerns within the transport layer itself. Even better, it also opens up a number of entirely new opportunities to optimize our applications and improve performance!

https://developers.google.com/web/fundamentals/performance/http2

Enable minification, optimizations, and uglification of code

enableProdMode() improves application performance by disabling development-only safety checks and debugging utilities, such as the expression-changed-after-checked detection. Building your application with the production configuration automatically enables Angular's runtime production mode.

https://angular.io/guide/deployment#enable-prod-mode

Enable gzip and brotli compression

Text-based resources should be served with compression to minimize total network bytes.

https://web.dev/uses-text-compression/

Enable proper Cache Control headers

The server can return a Cache-Control directive to specify how, and for how long, the browser and other intermediate caches should cache the individual response.

https://web.dev/http-cache/

Configure pre-fetching of resources, including fonts and critical styles

Adding <link rel=prefetch> to a web page tells the browser to download entire pages, or some of the resources (like scripts or CSS files), that the user might need in the future. This can improve metrics like First Contentful Paint and Time to Interactive and can often make subsequent navigations appear to load instantly.

https://web.dev/link-prefetch/

Configure CDN or other distributed services


Further reading...