Using the Ahead-of-Time (AOT) Compiler
Jan 23, 2020 • 8 Minute Read
Introduction
In this guide, we're going to learn about the ahead-of-time compiler, also known as AOT. This is an important topic that will impact the performance of your Angular application.
What is a Compiler
?
A compiler is nothing but a chunk of code that converts a programming language to another programming language. If we talk about C language, which is a very basic programming language, the compiler needs to convert the program from C to byte code to execute.
Machines don't know languages like C or Java. They only understand the language of bytes. That's why we need to convert code from one language to another language.
Browsers, however, don't understand byte code. They understand the languages of HTML, CSS, and JavaScript. They don't understand high-level languages like SASS, SCSS, or jQuery. These languages need to be converted into HTML, CSS or JavaScript.
The Compiler in Angular
An Angular application consists of components and their templates, provided by Angular to enhance productivity so you can make an app more quickly with high performance.
Browsers don't know the language of templates and components. As I said before, they only know the languages of HTML, CSS, and JavaScript. So we need to convert from a template to a JavaScript program. We need a compiler.
Angular has two types of compilers.:
- Just-in-Time (JIT)
- Ahead-of-Time (AOT)
Just-in-Time
Compiler
The just-in-time compiler does the compilation process at the time of rendering.
You can host the program at the localhost with the following command :
ng serve
If you have to host it somewhere like AWS, then you need to make a build of it.
You can make a build with the following command :
ng build --prod
--prod allows the application to take the configuration of the production level that has been configured in the Angular app. Angular checks the syntax of the Angular program and adds the angular compiler, which can translate a program from component to actual JavaScript.
The angular compiler build gets heavy, and it takes more time to download the program in the browser.
After download, Angular compiles the program, which also takes some time to translate from component to JavaScript. This affects in rendering time of the application in the browser.
The Ahead-of-Time (AOT)
Compiler
We can decrease the size of the build and compile it at the time of the build. This means the application can be quickly downloaded by the browser and doesn't need to spend the time to compile the program. This is possible with the ahead-of-time (AOT) compiler.
AOT compiles the program at the building level so the browser doesn't need to spend the time to compile it. The program is already compiled, and Angular doesn't add compiler into the build. This helps to decrease the size of the build.
You don't have to do much for the AOT compiler—just have some extra parameters in command of the JIT compiler.
If you have to host in local, hit the following command:
ng serve --aot
If you have to make a build to host it somewhere like AWS, hit the following command:
ng build --prod --aot
How AOT Works
The Angular AOT compiler extracts the metadata to interpret the Angular application in equivalent JavaScript code. It recognizes the code based on decorators like @Component, @Input, @Output, @ViewChild, etc.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
title = 'App component';
@Input() testInput;
}
As you can see, in the above example we have two decorators: @Component and @Input.
The Angular compiler treats both decorators differently and generates a factory for components. When it needs to create the instance of the component, it calls the factory, which produces a new element.
The Angular AOT Compiler compiles it in 3 phases :
- Code analysis
- Code generation
- Template type checking
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 it that recorded in 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.
@Component({
...
providers: [{provide: server, useFactory: () => new Server()}]
})
In this example, the provide keyword accepts an Injection token that should be unique and useFactory accepts a function that returns an instance of a service.
There is no error with the program, but the AOT compiler doesn't know the lambda expression and it throws an error. But since Angular version 5, it converts the program as below :
export function serverFactory() {
return new Server();
}
@Component({
...
providers: [{provide: server, useFactory: serverFactory}]
})
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.
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
- Conditional operator (expression?value1:value2)
Code Generation
The code collector only collects the code and gives the output in .metadata.json. It doesn't interpret the JavaScript code. In the second phase, this job gets done. The compiler does the job of code generation and throws an error if there are any semantic errors.
Some of the errors I would like to mention are public errors.
The variable used in the HTML template should be public in the TS file. Suppose we've used @Input for a data binding in the ts file, so it should be public.
<span>{{title}}</span>
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
title = 'App component';
}
As you can see in above code, I've used the title variable to display it in the HTML file. I kept a keyword as public. I didn't mention the public keyword because Typescript is set as public if no access specifier is mentioned.
Template Type Checking
After code generation, the compiler checks out the template type. It checks the variable and function in the typescript file that you've used in the HTML template to display or to apply the condition.
Suppose you have used an isEvent(n) function in the HTML template to check whether the number is even or odd. The AOT compiler checks it in typescript, and if it doesn't present, then it throws an error like isEvent is not a function.
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template:'<span *ngIf="isEven(2)">Even number</span>',
styleUrls: ['./app.component.scss'],
})
export class AppComponent {
title = 'App component';
}
In the above program, I've used the function isEven but haven't mentioned it in typescript. So the AOT compiler will throw the error.
After these three phases, the AOT compiler completes its job and makes a build if everything is written, otherwise it throws the error.
Although the Angular AOT compiler checked all the conditions and interpreted them at the time of build, it doesn't add the compiler into the build, which helps to make the build smaller.
Conclusion
Thanks for reading this article. I hope it has helped you to understand the AOT concept more clearly. You can read more about it here.