An Angular application consists largely of components and their HTML templates.
Before the browser can render the application,
the components and templates must be converted to executable JavaScript by the Angular compiler.
You can compile the app in the browser, at runtime, as the application loads, using the just-in-time (JIT) compiler.
This is the standard development approach shown throughout the documentation.
It's great but it has shortcomings.
JIT compilation incurs a runtime performance penalty.
Views take longer to render because of the in-browser compilation step.
The application is bigger because it includes the Angular compiler
and a lot of library code that the application won't actually need.
Bigger apps take longer to transmit and are slower to load.
编译可以发现一些组件模板绑定错误。JIT编译在运行时才揭露它们,那样有点太晚了。
Compilation can uncover many component-template binding errors.
JIT compilation discovers them at runtime, which is late in the process.
而预编译(AOT)会在构建时编译,这样可以在早期截获模板错误,提高应用性能。
The ahead-of-time (AOT) compiler can catch template errors early and improve performance
by compiling at build time.
There is actually only one Angular compiler. The difference between AOT and JIT is a matter of timing and tooling.
With AOT, the compiler runs once at build time using one set of libraries;
with JIT it runs every time for every user at runtime using a different set of libraries.
With AOT, the browser downloads a pre-compiled version of the application.
The browser loads executable code so it can render the application immediately, without waiting to compile the app first.
The compiler inlines external HTML templates and CSS style sheets within the application JavaScript,
eliminating separate ajax requests for those source files.
There's no need to download the Angular compiler if the app is already compiled.
The compiler is roughly half of Angular itself, so omitting it dramatically reduces the application payload.
提早检测模板错误
Detect template errors earlier
AOT编译器在构建过程中检测和报告模板绑定错误,避免用户遇到这些错误。
The AOT compiler detects and reports template binding errors during the build step
before users can see them.
AOT compiles HTML templates and components into JavaScript files long before they are served to the client.
With no templates to read and no risky client-side HTML or JavaScript evaluation,
there are fewer opportunities for injection attacks.
Preparing for offline compilation takes a few simple steps.
Take the Setup as a starting point.
A few minor changes to the lone app.component lead to these two class and HTML files:
<button (click)="toggleHeading()">Toggle Heading</button><h1 *ngIf="showHeading">Hello Angular</h1><h3>List of Heroes</h3><div *ngFor="let hero of heroes">{{hero}}</div>
ngc requires its own tsconfig.json with AOT-oriented settings.
Copy the original src/tsconfig.json to a file called tsconfig-aot.json on the project root,
then modify it as follows.
The compilerOptions section is unchanged except for one property.
Set the module to es2015.
This is important as explained later in the Tree Shaking section.
What's really new is the ngc section at the bottom called angularCompilerOptions.
Its genDir property tells the compiler
to store the compiled output files in a new aot folder.
The "skipMetadataEmit" : true property prevents the compiler from generating metadata files with the compiled application.
Metadata files are not necessary when targeting TypeScript files, so there is no reason to include them.
The AOT compiler requires that @Component URLS for external templates and CSS files be component-relative.
That means that the value of @Component.templateUrl is a URL value relative to the component class file.
For example, an 'app.component.html' URL means that the template file is a sibling of its companion app.component.ts file.
而JIT应用的URL更灵活,固定写成相对于组件的URL的形式对AOT编译的兼容性也更好。
While JIT app URLs are more flexible, stick with component-relative URLs for compatibility with AOT compilation.
编译该应用
Compiling the application
在命令行中执行下列命令,借助刚安装好的ngc编译器来启动AOT编译:
Initiate AOT compilation from the command line using the previously installed ngc compiler by executing:
node_modules/.bin/ngc -p tsconfig-aot.json
Windows用户应该双引号ngc命令:
Windows users should surround the ngc command in double quotes:
After ngc completes, look for a collection of NgFactory files in the aot folder.
The aot folder is the directory specified as genDir in tsconfig-aot.json.
These factory files are essential to the compiled application.
Each component factory creates an instance of the component at runtime by combining the original class file
and a JavaScript representation of the component's template.
Note that the original component class is still referenced internally by the generated factory.
Open main.ts and convert it to AOT compilation.
Switch from the platformBrowserDynamic.bootstrap used in JIT compilation to
platformBrowser().bootstrapModuleFactory and pass in the AOT-generated AppModuleNgFactory.
这里是AOT版本main.ts中的引导过程,下一个是你所熟悉的JIT版本。
Here is AOT bootstrap in main.ts next to the original JIT version:
AOT compilation sets the stage for further optimization through a process called tree shaking.
A tree shaker walks the dependency graph, top to bottom, and shakes out unused code like
dead leaves in a tree.
Tree shaking can greatly reduce the downloaded size of the application
by removing unused portions of both source and library code.
In fact, most of the reduction in small apps comes from removing unreferenced Angular features.
For example, this demo application doesn't use anything from the @angular/forms library.
There is no reason to download forms-related Angular code and tree shaking ensures that you don't.
Tree shaking and AOT compilation are separate steps.
Tree shaking can only target JavaScript code.
AOT compilation converts more of the application to JavaScript,
which in turn makes more of the application "tree shakable".
Rollup
这个烹饪宝典中用来示范的摇树优化工具是Rollup。
This cookbook illustrates a tree shaking utility called Rollup.
Rollup statically analyzes the application by following the trail of import and export statements.
It produces a final code bundle that excludes code that is exported, but never imported.
Rollup只能对ES2015模块摇树,因为那里有import和export语句。
Rollup can only tree shake ES2015 modules which have import and export statements.
Recall that tsconfig-aot.json is configured to produce ES2015 modules.
It's not important that the code itself be written with ES2015 syntax such as class and const.
What matters is that the code uses ES import and export statements rather than require statements.
通过下列命令安装Rollup依赖:
In the terminal window, install the Rollup dependencies with this command:
Next, create a configuration file (rollup-config.js)
in the project root directory to tell Rollup how to process the application.
The cookbook configuration file looks like this.
rollup-config.js
import rollup from 'rollup'import nodeResolve from 'rollup-plugin-node-resolve'import commonjs from 'rollup-plugin-commonjs';import uglify from 'rollup-plugin-uglify'exportdefault{
entry:'src/main.js',
dest:'src/build.js',// output a single application bundle
sourceMap:false,
format:'iife',
onwarn:function(warning){// Skip certain warnings// should intercept ... but doesn't in some rollup versionsif( warning.code ==='THIS_IS_UNDEFINED'){return;}// console.warn everything else
console.warn( warning.message );},
plugins:[
nodeResolve({jsnext:true, module:true}),
commonjs({
include:'node_modules/rxjs/**',}),
uglify()]}
This config file tells Rollup that the app entry point is src/app/main.js .
The dest attribute tells Rollup to create a bundle called build.js in the dist folder.
It overrides the default onwarn method in order to skip annoying messages about the AOT compiler's use of the this keyword.
下一节我们将深入讲解插件。
The next section covers the plugins in more depth.
Rollup插件
Rollup Plugins
这些可选插件过滤并转换Rollup的输入和输出。
Optional plugins filter and transform the Rollup inputs and output.
Rollup expects application source code to use ES2015 modules.
Not all external dependencies are published as ES2015 modules.
In fact, most are not. Many of them are published as CommonJS modules.
Luckily, there is a Rollup plugin that modifies RxJs
to use the ES import and export statements that Rollup requires.
Rollup then preserves the parts of RxJS referenced by the application
in the final bundle. Using it is straigthforward. Add the following to
the plugins array in rollup-config.js:
Rollup tree shaking reduces code size considerably. Minification makes it smaller still.
This cookbook relies on the uglify Rollup plugin to minify and mangle the code.
Add the following to the plugins array:
rollup-config.js (CommonJs to ES2015 Plugin)
uglify()
在生产环境中,我们还应该打开Web服务器的gzip特性来把代码压缩得更小。
In a production setting, you would also enable gzip on the web server to compress
the code into an even smaller package going over the wire.
运行Rollup
Run Rollup
通过下列命令执行Rollup过程:
Execute the Rollup process with this command:
node_modules/.bin/rollup -c rollup-config.js
Windows用户要把rollup命令放进双引号中:
Windows users should surround the rollup command in double quotes:
Loading the generated application bundle does not require a module loader like SystemJS.
Remove the scripts that concern SystemJS.
Instead, load the bundle file using a single <script> tag after the </body> tag:
index.html (load bundle)
<scriptsrc="build.js"></script>
启动应用服务器
Serve the app
你需要一个Web服务器来作为应用的宿主。
像与文档中其它部分一样,用lite-server吧:
You'll need a web server to host the application.
Use the same lite-server employed elsewhere in the documentation:
npm run lite
启动了服务器、打开浏览器,应用就出现了。
The server starts, launches a browser, and the app should appear.
AOT compilation and rollup together take several seconds.
You may be able to develop iteratively a little faster with SystemJS and JIT.
The same source code can be built both ways. Here's one way to do that.
复制一份index.html并命名为index-jit.html。
Make a copy of index.html and call it index-jit.html.
删除index-jit.html底部用来加载bundle.js的脚本
Delete the script at the bottom of index-jit.html that loads bundle.js
Notice the slight change to the system.import which now specifies src/app/main-jit.
That's the JIT version of the bootstrap file that we preserved above.
打开另一个终端窗口,并输入npm start:
Open a different terminal window and enter npm start.
That compiles the app with JIT and launches the server.
The server loads index.html which is still the AOT version, which you can confirm in the browser console.
Change the address bar to index-jit.html and it loads the JIT version.
This is also evident in the browser console.
Develop as usual.
The server and TypeScript compiler are in "watch mode" so your changes are reflected immediately in the browser.
要对比AOT版的变化,可以切换到原来的终端窗口中,并重新运行npm run build:aot。
结束时,回到浏览器中,并用浏览器的后退按钮回到默认index.html中的AOT版本。
To see those changes in AOT, switch to the original terminal and re-run npm run build:aot.
When it finishes, go back to the browser and use the back button to
return to the AOT version in the default index.html.
The sample above is a trivial variation of the QuickStart application.
In this section you apply what you've learned about AOT compilation and tree shaking
to an app with more substance, the Tour of Heroes application.
Today AOT compilation and tree shaking take more time than is practical for development. That will change soon.
For now, it's best to JIT compile in development and switch to AOT compilation before deploying to production.
幸运的是,如果你处理了几个关键不同点,源代码可以在没有任何变化时,采取两种方式的任何一种都能编译。
Fortunately, the source code can be compiled either way without change if you account for a few key differences.
index.html
JIT和AOT应用的设置和加载非常不一样,因此它们需要各自的index.html文件。
The JIT and AOT apps require their own index.html files because they setup and launch so differently.
The JIT version relies on SystemJS to load individual modules.
Its scripts appear in its index.html.
The AOT version loads the entire application in a single script, aot/dist/build.js.
It does not need SystemJS, so that script is absent from its index.html
JIT and AOT applications boot in much the same way but require different Angular libraries to do so.
The key differences, covered in the Bootstrap section above,
are evident in these main files which can and should reside in the same folder:
JIT-compiled applications transpile to commonjs modules.
AOT-compiled applications transpile to ES2015/ES6 modules to facilitate tree shaking.
AOT requires its own TypeScript configuration settings as well.
你将需要单独的TypeScript配置文件,像这些:
You'll need separate TypeScript configuration files such as these:
In the file structure of this particular sample project,
the node_modules folder happens to be two levels up from the project root.
Therefore, "typeRoots" must be set to "../../node_modules/@types/".
In a more typical project, node_modules would be a sibling of tsconfig-aot.json
and "typeRoots" would be set to "node_modules/@types/".
Edit your tsconfig-aot.json to fit your project's file structure.
摇树优化
Tree shaking
Rollup和以前一样,仍然进行摇树优化。
Rollup does the tree shaking as before.
rollup-config.js
import rollup from 'rollup'import nodeResolve from 'rollup-plugin-node-resolve'import commonjs from 'rollup-plugin-commonjs';import uglify from 'rollup-plugin-uglify'//paths are relative to the execution pathexportdefault{
entry:'src/main-aot.js',
dest:'aot/dist/build.js',// output a single application bundle
sourceMap:true,
sourceMapFile:'aot/dist/build.js.map',
format:'iife',
onwarn:function(warning){// Skip certain warnings// should intercept ... but doesn't in some rollup versionsif( warning.code ==='THIS_IS_UNDEFINED'){return;}// console.warn everything else
console.warn( warning.message );},
plugins:[
nodeResolve({jsnext:true, module:true}),
commonjs({
include:['node_modules/rxjs/**']}),
uglify()]}
运行应用
Running the application
面向大众的运行AOT构建的英雄指南应用的说明还没有准备好。
The general audience instructions for running the AOT build of the Tour of Heroes app are not ready.
The following instructions presuppose that you have cloned the
angular.io
github repository and prepared it for development as explained in the repo's README.md.
英雄指南源代码在public/docs/_examples/toh-6/ts目录。
The Tour of Heroes source code is in the public/docs/_examples/toh-6/ts folder.
和其他JIT例子一样,使用npm start命令,运行JIT编译的应用:
Run the JIT-compiled app with npm start as for all other JIT examples.
AOT编译假设上面介绍的一些支持文件都以准备好。
Compiling with AOT presupposes certain supporting files, most of them discussed above.
It's fascinating to see what the generated JavaScript bundle looks like after Rollup.
The code is minified, so you won't learn much from inspecting the bundle directly.
But the source-map-explorer
tool can be quite revealing.
The source-map-explorer analyzes the source map generated with the bundle and draws a map of all dependencies,
showing exactly which application and Angular modules and classes are included in the bundle.