译文 | Angular中的AoT编译( 五 )


另外 , Host1 则负责和本身的渲染 。
这个例子可以以下面这个图来总结:
AoT vs JiT 开发体验
这个小结 , 我们来讨论使用AoT开发和JiT开发的另一种体验 。
可能使用JiT对开发体验的冲击最大的就是JiT模式为 和 host 生成的是代码 , 这意味着组件的控制器中的属性都是的 , 因此我们不会得到任何编译错误 。
在JiT模式下 , 一旦我们启动了应用 , 根组件的根注入器和所有的指令就已经准备就绪了(他们被包含在和其他所有我们在根模块中引入的模块中了) 。元数据信息会传递给编译器 , 用于对根组件的模板的编译 。一旦编译器生成了JiT下的代码 , 编译器就拥有了用于生成各个子组件的所有元数据信息 。由于编译器此时不仅知道了当前层级的组件有那些可用 , 还可以知道那些指令是可见的 , 因此它可以给所有的组件生成代码 。
这一点让编译器在访问了模板中的一个元素时 , 知道该怎么工作 。根据是否有选择器是 bar-baz的指令/组件 ,  这样的一个元素就有了两种不同的解释 。编译器在创建了这样的一个元素的同时 , 是否还同时初始化 bar-baz 对应的组件类的实例 , 则完全取决于当前阶段的编译过程的元数据信息 。
这里有一个问题 , 在编译阶段 , 我们如何知道指令在整个组件树上是否可访问?得益于框架的良好设计 , 我们通过静态代码分析就可以做到 。Chuck和 Alex Eagle 在这个方向上做出了令人惊叹的成果 , 他们实现了和相关的模块 。所做的事情就是通过遍历组件树来获取每个组件和的元数据信息 , 这个过程中 , 很多牛逼的技术被用到 , 可惜这些技术超出了本文的范畴 。
AoT与第三方模块
为了编译组件的模板 , 编译器需要组件的元数据信息 , 我们来假设我们的应用使用到了一些第三方组件 , 的AoT编译器是如何获取这些已经被编译成代码的组件的元数据信息的?这些组件库必须连带发布对应的 *..json 文件 , 这样才能够对一个引用了它的页面进行AoT编译 。
如果你想了解如何使用编译器 , 例如如何编译你自定义库使得他们能够被用于以AoT编译的应用 , 那请访问这个链接 #L52-L54
我们从AoT中获得了什么?
你可能已经想到了 , AoT给我们带来了性能的提升 。以AoT方式开发的应用的初次渲染性能要比JiT的高的多 , 这是由于JS虚拟机需要的计算量大大减少了 。我们只在开发过冲中 , 将组件的模板编译成js代码 , 在此之后 , 用户不需要等待再次编译 。
下面这个图中 , 可以看出JiT渲染方式在初始化过程中所消耗的时间:
下面这个图你可以看出AoT方式初始化在初始化过程中所消耗的时间:
编译器不仅能够生产 , 还能够生成 , 这一点还带给我们要给非常棒的特性:在模板中进行类型检查 。
由于应用的模板是纯/ , 我们可以精确的知道哪些东西在哪被用到了 , 这一点让我们可以对代码进行有效的摇树操作 , 它能够把所有的未使用过的指令/模块从我们生产环境中的应用代码包中给删除掉 。这首要的一点是 , 我们的应用程序代码包中 , 再也无需包含 @/ 这个模块 , 因为我们在应用的运行时根本就用不到它 。
有一点需要注意的是 , 一个中大型的应用代码包 , 在进行AoT编译过之后 , 可能会比使用JiT方式编译的代码包要大一些 。这是因为 ngc 生成的对JS虚拟机友好的代码比基于HTML模板的代码要冗长一些 , 并且这些代码还包含了脏检查逻辑 。如果你想降低你的应用代码的尺寸 , 你可以通过懒加载的方式来实现 , 内建的路由已经支持这一点了 。