Ahead-of-time (AOT) Compilation

Angular Sep 16, 2022

At Halodoc, we are using Angular framework for our web development. As we know, Angular is a highly popular web development framework that offers rich user experiences, fast responsiveness, and code maintainability. Angular 9   introduce the Ahead Of Time (AOT) compiler and set as run time by default.

In this article, we’ll focus on the process of how Ahead of time compilation helps in Application development Angular

Let’s look at the steps of Ahead of Time Compiler:
1. What is AOT
2. Why we use AOT
3. How AOT works
4. Compilation phases
5. What we get from AOT

What is AOT:
An Angular application mainly consists of components and their HTML templates. The components and templates provided by angular cannot be understood by the browser directly, so the angular applications require a compilation process before they can run in a browser.

The Angular ahead-of-time (AOT) compiler converts your Angular HTML and TypeScript code into efficient JavaScript code during the build phase before the browser downloads and runs that code. Compiling your application during the build process provides a faster rendering in the browser. This is the best compilation mode for production environments, with decreased load time and increased performance compared to just-in-time (JIT) compilation.

Why we use AOT:
There are four major reasons to opt the AOT compiler:

  1. Faster rendering:
    The browser downloads a pre-compiled version of the application with the help of AOT. The browser renders the application immediately by loading the executable code without waiting to compile the application first.
  2. Fewer asynchronous requests:
    The compiler eliminates separate ajax  request of those source file which contain the inlines external HTML templates and CSS style sheets within the application JavaScript.
  3. Detect template errors earlier:
    The AOT compiler detects and reports template binding errors during the build step before users can see them.
  4. Better security:
    AOT compiles HTML templates and components into JavaScript files long before they are served to the client. With no templates to read and no risky client-side HTML or JavaScript evaluation, there are fewer opportunities for injection attacks.

How AOT works:
The Angular AOT compiler extracts the metadata to interpret the Angular application in equivalent JavaScript code. You can specify the metadata explicitly in decorators such as @Component() and @Input(), or implicitly in the constructor declarations of the decorated classes. The metadata tells Angular how to construct instances of your application classes and interact with them at runtime.

In the following example, the @Component() metadata object and the class constructor tell angular how to create and display an instance of TypicalComponent.

Once the metadata is extracted by the angular compiler it will then generate the factory for TypicalComponent. When it needs to create a TypicalComponent instance then angular calls the factory in which it produces a new visual element, which bound to a new instance of the component class with its injected dependency.

Compilation phases:
There are three phases of AOT compilation are :-

Code Analysis:
The Typescript compiler does some of the analytics work in the first phase. After analytics, it emits the file with the extension of .d.ts that AOT Compiler needs to generate the application code. At the same time, AOT Collector collects the metadata and analyzes the recorded angular decorators like @Component, @NgModule, @Directive. Once the analyzing process is done, it emits the metadata information in .metadata.json files.

The AOT compiler only understands a subset of JavaScript. It doesn't know the whole JavaScript syntax. Suppose you want to make your own provider for a service, and you want to write the program as below.

In the above example, the AOT collector does not support the arrow function,      () => new Server(), in a metadata expression. It generates an error node in place of the function. When the compiler later interprets this node, it reports an error that invites you to turn the arrow function into an exported function.

We can fix the error by converting to this:

The Angular compiler also doesn't support the function or keyword that's not being exported. In the program above, you can see we have the same function that returning an instance of server and function is getting exported. In version 5 and later, the compiler automatically performs this rewriting while emitting the .js file.

The AOT compiler supports most syntax from JavaScript but not all. These are some of the syntaxes:

  • Literal object ({key1:value1, key2:value2})
  • Literal Array ([item1, item2, item3])
  • Null

Code Generation:
The code collector only collects the code (no attempts to understand the metadata) and gives the output in .metadata.json. It represents the metadata and record the errors when it detects a metadata syntax violation. It's the compiler's job to interpret the .metadata.json in the code generation phase. It throws an error if there are any semantic errors.

In the above example, we have used the title variable to display in the template and made it private. Now it throws an error i.e:

It shows that whatever we are using in the HTML template that should always declared as public variable in the ts file.

Template type checking:
In the angular compiler, it has the ability to type-check expressions within the HTML template and catch any errors before they cause crashes at runtime. In the template type checking phase, the angular template compiler uses the TypeScript compiler to validate the binding expressions in templates. When type errors are detected, the template validation produces appropriate error messages.

For example, consider the scenario of child and parent component:

The parent component template as follows:

In the above example, we are passing the value from parent component through user variable. While in the child component, created the instance of user variable by the help of @Input().

The type checking of the template for ParentComponent is [user]="selectedUser" binding corresponds with the ChildComponent.user input. Therefore, angular assigns the selectedUser property to ChildComponent.user, which would result in an error if their types were incompatible. TypeScript checks the assignment according to its type system, obeying flags such as strictNullChecks as they are configured in the application.

Avoid run-time type errors by providing more specific in-template type requirements to the template type checker. Make the input type requirements for your own directives as specific as possible by providing template-guard functions in the directive definition.

What we get from AoT:
AOT increases the performance of the development process. During the  initial rendering performance of each angular applications we develop with AoT will be much faster compared to a JIT. Since the  JVM (JavaScript Virtual Machine) needs to perform much less computations, so we compile the templates to JavaScript only once as part of our development process, after that the user gets compiled templates for free.

On the image below we can see how much time it takes to perform the initial rendering with JIT:

On the image below now we can see how much time it takes to perform the initial rendering with AoT:

Since the templates of the application are pure JavaScript/TypeScript, we know exactly what and where is used. This allows us to perform effective tree-shaking and drop all the directives/modules which are not used by the application. On top of that we don’t need to include the @angular/compiler module in the application bundle since we don’t need to perform compilation at runtime.

But for large to medium size applications the bundle produced after performing AoT compilation will most likely be bigger compared to same application using JIT compilation. This is because the VM friendly JavaScript produced by ngc is more verbose compared to the HTML-like templates, and also includes dirty-checking logic. In case you want to drop the size of the app you can perform lazy loading which is supported natively by the angular router.

Conclusion:
The AOT compiler improves the performance of our applications dramatically by taking the advantage of inline caching mechanism of the JavaScript Virtual Machines. On top of that we can perform it as part of our build process which solves problems such as forbidden eval, allows us to perform more efficient tree-shaking which improves the initial rendering time.

In this article, we have shared the overview of  all the phases and some binding example of AOT compiler that execute while building any angular application. The angular compiler can emit not only JavaScript but TypeScript as well. There are still lot of methods and functionalities comes under the type checking phase that have not been covered yet, which we plan to cover in our blogs in the future.


References:


Read More:
You can read more about other Web topics in our Halodoc blogs at https://blogs.halodoc.io/

Join US
Scalability, reliability, and maintainability are the three pillars that govern what we build at Halodoc Tech. We are actively looking for engineers at all levels and if solving hard problems with challenging requirements is your forte, please reach out to us with your resumé at careers.india@halodoc.com.

About Halodoc
Halodoc is the number 1 all-around Healthcare application in Indonesia. Our mission is to simplify and bring quality healthcare across Indonesia, from Sabang to Merauke. We connect 20,000+ doctors with patients in need through our Tele-consultation service. We partner with 3500+ pharmacies in 100+ cities to bring medicine to your doorstep. We've also partnered with Indonesia's largest lab provider to provide lab home services, and to top it off we have recently launched a premium appointment service that partners with 500+ hospitals that allow patients to book a doctor appointment inside our application. We are extremely fortunate to be trusted by our investors, such as the Bill & Melinda Gates Foundation, Singtel, UOB Ventures, Allianz, GoJek, Astra, Temasek, and many more. We recently closed our Series C round and In total have raised around USD$180 million for our mission. Our team works tirelessly to make sure that we create the best healthcare solution personalised for all of our patient's needs, and are continuously on a path to simplifying healthcare for Indonesia.


Abhishek Kesarwani

I am passionate about creating elegant and efficient code that solves real-world problems. I am also a strong team player and enjoy working with others to deliver high-quality software products.