预先编译器, 英文全称是 Ahead-of-time compiler。由于 Angular 9 新版本的到来,CLI 应用程序默认情况下以 AOT 模式进行编译,其中包括模板类型检查,因此,我觉得有必要好好理解并总结一下 AOT 编译器的原理和流程。如需参考官方文档请前往 Angular - 预先(AOT)编译器。
大家都知道,Angular 的应用主要是由 components 和 HTML templates 组成。components 和 HTML templates 是 declarative 的代码, 而浏览器只接受 imperative 的代码 ( JavaScript ),因此它们无法被浏览器直接理解。这时 Angular 就需要自己的 compiler 来编译这些 declarative 的代码。那么,我们该什么时候编译这些代码呢?
Angular 给出了两种方案/模式:即时编译( JIT ) 和 预先( AOT )编译。顾名思义,即时编译,也就是 Just-in-time,是指编译器会在 runtime 编译应用。而预先编译则是指在构建( build )应用的时候进行编译。JIT 很好理解,但是为什么 Angular 会需要 AOT 这个编译模式呢?
使用 AOT 的原因
- 更快的渲染速度。就像 AOT 模式的定义所讲的一样,由于 declarative 的代码会被预先编译,浏览器可以直接使用这些可以直接执行的 imperative 代码,立即给用户呈现应用。
- 更早检查出 template 错误。由于需要预编译,AOT compiler 会在构建阶段就检测到 template 的绑定错误,并把这些错误提前报告给我们写程序的人,而不是等到 runtime 编译才让用户发现这些错误。
- 更高的 client-side 安全性。由于 templates 和 components 在给 client side 接触到之前就被预先编译成了 JavaScript,client side 没有办法读取到 templates,HTML 和 JavaScript 的解析也不会存在很大的危险性,这样也让 Client-side injection attacks 也会变得更加困难。
- 需要下载的 Angular Framework size 变得更小。由于应用程序被预编译,Angular 编译器也就无需被下载。应用程序负荷( payload )大大减少。
- 更少的异步请求。AOT 编译器会内联 HTML template 和 CSS style sheets,其中的单独的 ajax 请求也会随之被消除。
知道了使用 AOT 的原因,我们现在可以来了解一下 AOT 的编译流程。
代码分析( Code Analysis )
首先,在代码分析阶段,AOT 收集器( collector )起到来关键的作用。顾名思义,AOT 收集器负责收集整理 Angular 装饰器( decorators )的 元数据( metadata )。完成对代码的分析之后,AOT 编译器会为每一个 .ts
文件生成 .d.ts
文件,.d.ts
文件又叫类型定义文件。该文件包含原 .ts
文件中属性方法的类型信息,它可以帮助编译器生成 imperative 代码。
AOT 收集器在该阶段会把收集到的 metadata 信息输出到 .metadata.json
的文件中。每个 .d.ts
文件对应一个 .metadata.json
文件。那么,Angular metadata 有什么作用呢?