Quantum Angular: Maximizing Performance by Removing Zone

Experiment: removing Zone from Angular with minimal effort, to boost runtime performance.

Photo by Guillaume Jaillet on Unsplash

As Angular developers, we owe Zone a great deal: it’s also thanks to this library that we can use Angular almost magically; in fact, most times we simply need to change a property and it just works, Angular re-renders our components, and the view is always up to date. Pretty cool.

In this article, I want to explore some ways in which the new Angular Ivy compiler (being released in version 9) will be able to make apps work without Zone much simpler than it was in the past.

As a result, I was able to increase the performance of an application under heavy load by a huge amount by adding as little overhead as possible using Typescript’s decorators.

Notice: the approaches explained in this article are only possible thanks to Angular Ivy and AOT enabled by default. This article is educational only and doesn’t aim to advertise the code described.

Tip: Use Bit (Github) to easily and gradually build Angular component libraries. Collaborate on reusable components across projects to speed up development, maintain a consistent UI and write more scalable code.

Example: Angular components on bit.dev

The case for using Angular without Zone

Wait a moment, though: is it worth disabling Zone as it allows us to effortlessly re-render our templates? Yes, it is incredibly useful, but as always, magic comes at a cost.

If your application needs a special performance target, disabling Zone can help deliver better performance for your application: an example of a case-scenario where performance can actually be game-changing is high-frequency updates, which is an issue I had while working on a real-time trading application, where a WebSocket was continuously sending messages to the client.

Removing Zone from Angular

Running Angular without Zone is pretty simple. The first step is to comment out or remove the import statement in the file polyfills.ts :

The second step is to Bootstrap the root module with the following options:

platformBrowserDynamic()

.bootstrapModule(AppModule, {

ngZone: 'noop'

})

.catch(err => console.error(err));

Angular Ivy: Manually Detecting Changes with ɵdetectChanges and ɵmarkDirty

Before we can start building our Typescript decorator, we need to see how Ivy allows us to bypass Zone and DI and trigger a change detection on a component by marking it dirty.

We can now use two more functions exported from @angular/core : ɵdetectChanges and ɵmarkDirty . These two functions are still to be used privately and are not stable, hence they are prefixed with the character ɵ .

Let’s see an example of how they can be used.

ɵ markDirty

This function will mark a component dirty (e.g. need re-rendering) and will schedule a change detection at some point in the future unless it is already marked dirty.

import { ɵmarkDirty as markDirty } from '@angular/core'; @Component({...})

class MyComponent {

setTitle(title: string) {

this.title = title;

markDirty(this);

}

}

ɵdetectChanges

For efficiency reasons, the internal documentation discourages the use of ɵdetectChanges and recommends using ɵmarkDirty instead. This function will synchronously trigger a change detection on the components and subcomponents.

import { ɵdetectChanges as detectChanges } from '@angular/core'; @Component({...})

class MyComponent {

setTitle(title: string) {

this.title = title;

detectChanges(this);

}

}

Automatically detecting changes with a Typescript Decorator

While the functions provided by Angular increase the Developer Experience by allowing us to bypass the DI, we may still be unhappy about the fact we need to import and manually call these functions in order to trigger a change detection.

In order to make automatic change detection easier, we can write a Typescript decorator that can do it for us. Of course, we have some limitations, as we will see, but in my case, it did the job.

Introducing the @observed decorator

In order to detect changes with minimal effort, we will build a decorator that can be applied in three ways:

to synchronous methods

to an Observable

to an Object