If you’ve ever wondered what Angular does with your component code and what actually ends up in the browser, this article is for you!
First, if you want to look at the resulting code after the compiler does its work, you can run ng serve, then open dist/project-name/browser/main-xxxxx.js, where xxxxx is a unique hash generated by the compiler, such as main-5ABRQQ7A.js.
This hash is present for cache-busting purposes. When you deploy a new app version, your main.js file gets a new unique name, forcing the browser to download and use that new version instead of relying on the previous one. You can see it as an automatic version number that is generated for you.
In that main.js file, you’ll find something like this:
This code is your Angular runtime code in the browser. It’s almost impossible to read because it has been obfuscated (variables renamed and shortened to single letters) and minified (whitespace, tabs, and new lines removed).
This process makes our code as lightweight as possible, which means it’s faster to download, quicker to parse, and faster to run in a browser.
It’s also much more challenging to understand, so a hacker would have a harder time understanding your code.
Now, if we want to read that code so we can see what the compiler did to our components, we can update angular.json to disable that build optimization by setting optimization to false as follows:
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/compiler-experiment",
"optimization": false, <-- HERE
"index": "src/index.html",
This will output a lot more code, most likely going over your build-size budget, so you’ll also want to increase your budget temporarily to run that experiment:
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "5MB" <-- HERE increasing to 5MB
},
At this point, if we run ng serve again, our code becomes more readable, and if we look for our components by name, we can find them, for instance, AppComponent here:
// src/app/app.component.ts
var AppComponent = class _AppComponent {
title = "compiler-experiment";
static \u0275fac = function AppComponent_Factory(__ngFactoryType__) {
return new (__ngFactoryType__ || _AppComponent)();
};
For this experiment, my AppComponent is very simple:
import { Component } from '@angular/core';@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {
title = 'compiler-experiment';
}
And its template:
Welcome to {{title}}
The above component gets compiled into:
var AppComponent = class _AppComponent {
title = "compiler-experiment";
static \u0275fac = function AppComponent_Factory(__ngFactoryType__) {
return new (__ngFactoryType__ || _AppComponent)();
};
static \u0275cmp = /* @__PURE__ */ \u0275\u0275defineComponent({ type: _AppComponent, selectors: [["app-root"]], decls: 2, vars: 1, template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
\u0275\u0275elementStart(0, "h2");
\u0275\u0275text(1);j
\u0275\u0275elementEnd();
}
if (rf & 2) {
\u0275\u0275advance();
\u0275\u0275textInterpolate1(" Welcome to ", ctx.title, "");
}
}, encapsulation: 2 });
};
At first glance, we notice several things:
- Our HTML template was turned into JavaScript instructions that create the DOM.
- The compiler uses \u0275, which is Unicode character ɵ, in multiple places as a prefix for its functions and variables
For the sake of readability, let’s remove those characters in our compiler code, add some indentation, and look at it again:
var AppComponent = class _AppComponent {
title = "compiler-experiment";
static fac = function AppComponent_Factory(__ngFactoryType__) {
return new (__ngFactoryType__ || _AppComponent)();
};
static cmp = defineComponent(
{ type: _AppComponent,
selectors: [["app-root"]],
decls: 2, vars: 1,
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
elementStart(0, "h2");
text(1);
elementEnd();
}
if (rf & 2) {
advance();
textInterpolate1(" Welcome to ", ctx.title, "");
}
}, encapsulation: 2 });
};
In the above, we see that our TypeScript class properties are still present as such in our JavaScript code:
var AppComponent = class _AppComponent {
title = "compiler-experiment";
Then we notice that our HTML template was compiled into a template factory function with two different if blocks in it:
template: function AppComponent_Template(rf, ctx) {
if (rf & 1) {
elementStart(0, "h2");
text(1);
elementEnd();
}
if (rf & 2) {
advance();
textInterpolate1(" Welcome to ", ctx.title, "");
}
The first if block creates an empty h2 element. The second if block populates it with our template expression data.
As a result, the first block is designed to create the static DOM of our component, the parts that never change, once and for all.
The second block runs whenever our DOM needs to be updated, filling our HTML with actual data.
Let’s change our component template a little bit by adding a button and an event listener:
Welcome to {{title}}
The compiler translates the above into the following code, cleaned up from the \u0275\u0275 characters:
if (rf & 1) {
elementStart(0, "h2");
text(1);
elementEnd();
elementStart(2, "button", 0);
listener("click", function AppComponent_Template_button_click_2_listener() {
return ctx.title = "new title";
});
text(3, "Change text");
elementEnd();
}
if (rf & 2) {
advance();
textInterpolate1(" Welcome to ", ctx.title, "");
}
We can see that the click listener is created in the initial DOM creation block, and that the update code remains the same as earlier, as the only piece of HTML that can be updated is our welcome text.
Angular won’t even bother touching the rest of the DOM, which makes perfect sense and is the best option to update only what needs updating.
We can also tell that the component class doesn’t know anything about change detection, all it knows is:
- How to render the initial DOM
- How to update the parts of the DOM that can change
So that’s our first dive into the Angular compiler from a component perspective. I didn’t want to go too deep here as it’s not necessarily useful, but this post gives you an idea of what’s going on with your code.
If you’d like me to keep going deeper with this topic and explore more with the compiler in different scenarios, please let me know in the comments.
My name is Alain Chautard. I am a Google Developer Expert in Angular, Microsoft MVP, and a consultant and trainer at Angular Training, where I help web development teams learn and become proficient with Angular / React / JavaScript.
If you need any help learning web technologies, feel free to get in touch!
If you enjoyed this article, please clap for it or share it. Your help is always appreciated. You can also subscribe to my articles and Weekly Angular Newsletter to receive helpful weekly tips and updates about Angular.