译文 | Angular中的AoT编译
Posted RDK快速开发套件
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了译文 | Angular中的AoT编译相关的知识,希望对你有一定的参考价值。
译者:陈旭@中兴RDK
前两天,Jigsaw七巧板上来了个issue https://github.com/rdkmaster/jigsaw/issues/113,@jackjoy 在issue中提到了一篇介绍Angular AoT的文章,我看了一下,觉得讲的非常好,还涉及到一些Angular编译原理的内容。于是打算翻译一下,让大伙都能够读一读,多了解一点AoT知识。
文中的第一人称“我”均指代作者本人(http://blog.mgechev.com)
最近我给angular-seed增加了对Ahead-of-Time(AoT)编译的支持,这引来了不少关于这个新特性的问题。我们从下面这些话题开始来回答这些问题:
为什么Angular需要编译?
什么东西会被编译?
他们是如何被编译的?
编译发生在什么时候?JiT vs AoT
我们从AoT中获得了什么?
AoT编译是如何工作的?
我们使用AoT和JiT的代价是什么?
为什么Angular需要编译?
这个问题的简短回答是:编译可以让Angular应用达到更高层度的运行效率,我所说的效率,主要是指的性能提升,但也包括电池节能和节省流量。
AngularJs1.x 有一个实现渲染和变化检测的很动态的方式,比如AngularJs1.x的编译器非常通用,它被设计为任何模板实现一系列的动态计算,虽然它在通常情况下运行的很好,但是JS虚拟机的动态特性让一些低层次的计算优化变得很困难。由于js虚拟机无法理解那些我们作为脏检查的上下文对象(术语为scope)的形态,虚拟机的内联缓存常常不精确,这导致了运行效率的下降。
译者:scope是AngularJs1.x中的一个重要对象,他是AngularJs1.x用于计算模板的上下文。
Angular2+采用了一个不同的方式。在给每个组件做渲染和变化检测的时候,它不再使用同一套逻辑,框架在运行时或者编译时会生成对js虚拟机友好的代码。这些友好的代码可以让js虚拟机在属性访问的缓存,执行变化检查,进行渲染的逻辑执行的快的多。
举个例子,看看下面的代码:
// ...
Scope.prototype.$digest = function () {
'use strict';
var dirty, watcher, current, i;
do {
dirty = false;
for (i = 0; i < this.$$watchers.length; i += 1) {
watcher = this.$$watchers[i];
current = this.$eval(watcher.exp);
if (!Utils.equals(watcher.last, current)) {
watcher.last = Utils.clone(current);
dirty = true;
watcher.fn(current);
}
}
} while (dirty);
for (i = 0; i < this.$$children.length; i += 1) {
this.$$children[i].$digest();
}
};
// ...
这个代码片段来自《轻量级angularJs1.x实现》一文。这些代码实现了对scope树做深度优先搜索,目的是为了寻找绑定数据的变化,这个方法对任何指令都生效。这些代码显然比下面这些直接指定检查的代码慢:
// ...
var currVal_6 = this.context.newName;
if (import4.checkBinding(throwOnChange, this._expr_6, currVal_6)) {
this._NgModel_5_5.model = currVal_6;
if ((changes === null)) {
(changes = {});
}
changes['model'] = new import7.SimpleChange(this._expr_6, currVal_6);
this._expr_6 = currVal_6;
}
this.detectContentChildrenChanges(throwOnChange);
// ...
译者:
这里一下子提及了angularJs1.x的好几个概念,包括scope,数据绑定,指令。不熟悉angularJs1.x的同学理解起来费劲,想弄懂的话,自行搜索吧。个人认为可以无视,毕竟这个文章的重点不是在这里。你就认为Angular2+的处理方式比angularJs1.x牛逼很多就好了,哈哈。
上面代码包含了一个来自angular-seed的某个编译后的组件的代码,这些代码是由编译器生成的,包含了一个 detectChangesInternal
方法的实现。Angular框架通过直接属性访问的方式读取了数据绑定中的某些值,并且采用了最高效的方式与新的值做比较。一旦Angular框架发现这些值发生了变化,它就立即更新只受这些数据波及的DOM元素。
在回答了“为什么Angular需要编译”这个问题的同时,我们同时也回答了“什么东西会被编译”这个问题。我们希望把组件的模板编译成一个JS类,这些类包含了在绑定的数据中检测变化和渲染UI的逻辑。通过这个方式,我们和潜在的平台解耦了。换句话说,通过对渲染器采取了不同的实现,我们在不对代码做任何的修改的前提下,就可以对同一个以AoT方式编译的组件做不同的渲染。比如,上述代码中的组件还可以直接用在NativeScript中,这是由于这些不同的渲染器都能够理解编译后的组件。
编译发生在什么时候?JiT 还是 AoT
Angular编译器最cool的一点是它可以在页面运行时(例如在用户的浏览器内)启动,也可以作为构建的一个步骤在页面的编译时启动。这主要得益于Angular的可移植性:我们可以在任何的平台的JS虚拟机上运行Angular,所以我们完全可以在浏览器和NodeJs中运行它。
JiT编译模式的流程
一个典型的非AoT应用的开发流程大概是:
使用TypeScript开发Angular应用
使用
tsc
来编译这个应用的ts代码打包
压缩
部署
以上是关于译文 | Angular中的AoT编译的主要内容,如果未能解决你的问题,请参考以下文章
在 Angular 5 和 AOT-Build 中使用 @angular 编译器时出错