I am diving into angular 4 and I am trying to understand the compilation. I\'ve read that AOT and JIT both compile TypeScript to JavaScript whether that is server side or on
First of all angular is moving away from JIT compilation. I hope we will see it in angular@5.x.x
Angular compiler takes all metadata you write by using decorators like
@Component({
selector: 'my-app',
template: 'Hello
'm
styles: [ ':host { display: block }' ]
})
constructor(
@Host() @Optional() private parent: Parent,
@Attribute('name') name: string) {}
@ViewChild('ref') ref;
@ContentChildren(MyDir) children: QueryList;
@HostBinding('title') title;
@HostListener('click') onClick() { ... }
// and so on
and analizes it. Then it takes template and stylesheets and parses it. Compiler goes through many steps that i won't describe here. You can take a look at the following page that describes the compilation process. There is also great talk from Tobias Bosch. Finally compiler creates ngfactories to instantiate our application.
I think main differences between AOT in JIT are
ngfactory that compiler producesruns on client side in our browser on every page load.
It collects metadata by using ReflectionCapabilities API from @angular/core package. We have the following options to work with metadata in JIT mode:
1) direct API
for example we can declare our component like
export class AppComponent {
static annotations = [
new Component({
selector: 'my-app',
templateUrl: `./app.component.html`,
styles: [ ':host { display: block }' ]
})
];
test: string;
static propMetadata = {
test: [new HostBinding('title')]
};
ngOnInit() {
this.test = 'Some title'
}
}
Similar code we can write in ES5. JIT compiler will read annotations and propMetadata static properties. AOT compiler won't work with it.
2) tsickle API
export class AppComponent {
static decorators = [{
type: Component,
args: [{
selector: 'my-app',
templateUrl: `./app.component.html`,
styles: [ ':host { display: block }' ]
},]
}];
test: string;
static propDecorators = {
'test': [{ type: HostBinding, args: ['title'] }]
};
ngOnInit() {
this.test = 'Some title'
}
}
The code above usually is generated by some library. Angular package also has the same format. This also won't work with aot. We have to ship metadata.json file with our library for AOT compilation.
3) getting metadata created by invoking the decorators
@Component({
selector: 'my-app',
templateUrl: `./app.component.html`
})
export class AppComponent {
@HostBinding('title') test = 'Some title';
}
Typescript compiler transforms the preceding code to
var AppComponent = (function () {
function AppComponent() {
this.test = 'Some title';
}
return AppComponent;
}());
__decorate([
HostBinding('title')
], AppComponent.prototype, "test", void 0);
AppComponent = __decorate([
Component({
selector: 'my-app',
templateUrl: "./app.component.html"
})
], AppComponent);
this code is executed in JIT mode so angular calls Component decorator
const TypeDecorator: TypeDecorator = function TypeDecorator(cls: Type) {
// Use of Object.defineProperty is important since it creates non-enumerable property which
// prevents the property is copied during subclassing.
const annotations = cls.hasOwnProperty(ANNOTATIONS) ?
(cls as any)[ANNOTATIONS] :
Object.defineProperty(cls, ANNOTATIONS, {value: []})[ANNOTATIONS];
annotations.push(annotationInstance);
return cls;
};
Today it doesn't use Reflect api anymore. Compiler reads data directly from __annotations__ property
if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) {
return (typeOrFunc as any)[ANNOTATIONS];
}
JIT compiler produces javascript ngfactories
runs on server side (nodejs) at build time by using ngc.
With AOT, there is no runtime compile step. When we run our application in browser we have already precompiled ngfactories. It gives us better performance at first and lazy load. We also don't ship @angular/compiler code in our production bundle anymore. But our bundle can grow significantly because of our ngfactories code.
AOT compiler uses typescript api to analize typescript code. To get metadata compiler goes through StaticSymbolResolver and MetadataCollector APIs.
So it takes app.component.ts file and creates typescript object model. So our AppComponent class will be presented like NodeObject with type 229 (ClassDeclaration)
as we can see this object has decorators property
and special typescript wrapper written by angular team and called tsc-wrapper does hard work to extract this metadata.
When compiler meets d.ts file it is trying to get metadata from metadata.json:
if (DTS.test(filePath)) {
var metadataPath = filePath.replace(DTS, '.metadata.json');
if (this.context.fileExists(metadataPath)) {
return this.readMetadata(metadataPath, filePath);
}
else {
// If there is a .d.ts file but no metadata file we need to produce a
// v3 metadata from the .d.ts file as v3 includes the exports we need
// to resolve symbols.
return [this.upgradeVersion1Metadata({ '__symbolic': 'module', 'version': 1, 'metadata': {} }, filePath)];
}
}
And finally AOT compiler uses TypeScriptEmitter to produce typescript ngfactories (angular < 4.4.0)
See also