docs(aot-cookbook): copy edits applying guidelines, update TOCs (#3416)

This commit is contained in:
Kapunahele Wong 2017-03-23 18:20:59 -04:00 committed by Ward Bell
parent 9fb57f6b98
commit a5464d45a0
2 changed files with 104 additions and 78 deletions

View File

@ -7,7 +7,7 @@
"aot-compiler": { "aot-compiler": {
"title": "Ahead-of-Time Compilation", "title": "Ahead-of-Time Compilation",
"intro": "Learn how to use Ahead-of-time compilation." "intro": "Learn how to use ahead-of-time compilation."
}, },
"ajs-quick-reference": { "ajs-quick-reference": {

View File

@ -1,38 +1,48 @@
include ../_util-fns include ../_util-fns
:marked :marked
This cookbook describes how to radically improve performance by compiling _Ahead of Time_ (AOT) This cookbook describes how to radically improve performance by compiling _ahead-of-time_ (AOT)
during a build process. during a build process.
a#toc a#toc
:marked :marked
## Table of Contents # Contents
* [Overview](#overview) - [Overview](overview)
* [_Ahead-of-Time_ vs _Just-in-Time_](#aot-jit) - [Ahead-of-time (AOT) vs just-in-time (JIT)](#aot-jit)
* [Compile with AOT](#compile) - [Why do AOT compilation?](#why-aot)
* [Bootstrap](#bootstrap) - [Compile with AOT](#compile)
* [Tree Shaking](#tree-shaking) - [Bootstrap](#bootstrap)
* [Load the bundle](#load) - [Tree shaking](#tree-shaking)
* [Serve the app](#serve) - [Rollup](#rollup)
* [Workflow and convenience script](#workflow) - [Rollup Plugins](#rollup-plugins)
* [Source Code](#source-code) - [Run Rollup](#run-rollup)
* [Tour of Heroes](#toh) - [Load the bundle](#load)
- [Serve the app](#serve)
- [AOT QuickStart source code](#source-code)
- [Workflow and convenience script](#workflow)
- [Develop JIT along with AOT](#run-jit)
- [Tour of Heroes](#toh)
- [JIT in development, AOT in production](#jit-dev-aot-prod)
- [Tree shaking](#shaking)
- [Running the application](#running-app)
- [Inspect the Bundle](#inspect-bundle)
a#overview a#overview
.l-main-section .l-main-section
:marked :marked
## Overview ## Overview
An Angular application consist largely of components and their HTML templates. An Angular application consists largely of components and their HTML templates.
Before the browser can render the application, Before the browser can render the application,
the components and templates must be converted to executable JavaScript by the _Angular compiler_. the components and templates must be converted to executable JavaScript by the _Angular compiler_.
.l-sub-section .l-sub-section
:marked :marked
<a href="https://www.youtube.com/watch?v=kW9cJsvcsGo" target="_blank">Watch compiler author Tobias Bosch explain the Angular Compiler</a> at AngularConnect 2016. <a href="https://www.youtube.com/watch?v=kW9cJsvcsGo" target="_blank">Watch compiler author Tobias Bosch explain the Angular Compiler</a> at AngularConnect 2016.
:marked :marked
You can compile the app in the browser, at runtime, as the application loads, using the **_Just-in-Time_ (JIT) 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. This is the standard development approach shown throughout the documentation.
It's great .. but it has shortcomings. It's great but it has shortcomings.
JIT compilation incurs a runtime performance penalty. JIT compilation incurs a runtime performance penalty.
Views take longer to render because of the in-browser compilation step. Views take longer to render because of the in-browser compilation step.
@ -41,22 +51,23 @@ a#overview
Bigger apps take longer to transmit and are slower to load. Bigger apps take longer to transmit and are slower to load.
Compilation can uncover many component-template binding errors. Compilation can uncover many component-template binding errors.
JIT compilation discovers them at runtime which is later than we'd like. JIT compilation discovers them at runtime, which is late in the process.
The **_Ahead-of-Time_ (AOT) compiler** can catch template errors early and improve performance The **_ahead-of-time_ (AOT) compiler** can catch template errors early and improve performance
by compiling at build time as you'll learn in this chapter. by compiling at build time.
a#aot-jit a#aot-jit
.l-main-section .l-main-section
:marked :marked
## _Ahead-of-time_ (AOT) vs _Just-in-time_ (JIT) ## _Ahead-of-time_ (AOT) vs _just-in-time_ (JIT)
There is actually only one Angular compiler. The difference between AOT and JIT is a matter of timing and tooling. 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 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 JIT it runs every time for every user at runtime using a different set of libraries.
a#why-aot
### Why do AOT compilation? :marked
## Why do AOT compilation?
*Faster rendering* *Faster rendering*
@ -65,7 +76,7 @@ a#aot-jit
*Fewer asynchronous requests* *Fewer asynchronous requests*
The compiler _inlines_ external html templates and css style sheets within the application JavaScript, The compiler _inlines_ external HTML templates and CSS style sheets within the application JavaScript,
eliminating separate ajax requests for those source files. eliminating separate ajax requests for those source files.
*Smaller Angular framework download size* *Smaller Angular framework download size*
@ -91,9 +102,9 @@ a#compile
:marked :marked
## Compile with AOT ## Compile with AOT
### Prepare for offline compilation Preparing for offline compilation takes a few simple steps.
Take the <a href='../guide/setup.html'>Setup</a> as a starting point. Take the <a href='../guide/setup.html'>Setup</a> as a starting point.
A few minor changes to the lone `app.component` lead to these two class and html files: A few minor changes to the lone `app.component` lead to these two class and HTML files:
+makeTabs( +makeTabs(
`cb-aot-compiler/ts/src/app/app.component.html, `cb-aot-compiler/ts/src/app/app.component.html,
@ -114,8 +125,8 @@ code-example(language="none" class="code-shell").
`ngc` is a drop-in replacement for `tsc` and is configured much the same way. `ngc` is a drop-in replacement for `tsc` and is configured much the same way.
`ngc` requires its own `tsconfig.json` with AOT-oriented settings. `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), Copy the original `src/tsconfig.json` to a file called `tsconfig-aot.json` on the project root,
then modify it to look as follows. then modify it as follows.
+makeExample('cb-aot-compiler/ts/tsconfig-aot.json', null, 'tsconfig-aot.json')(format='.') +makeExample('cb-aot-compiler/ts/tsconfig-aot.json', null, 'tsconfig-aot.json')(format='.')
@ -125,23 +136,23 @@ code-example(language="none" class="code-shell").
This is important as explained later in the [Tree Shaking](#tree-shaking) section. This is important as explained later in the [Tree Shaking](#tree-shaking) section.
What's really new is the `ngc` section at the bottom called `angularCompilerOptions`. What's really new is the `ngc` section at the bottom called `angularCompilerOptions`.
Its `"genDir"` property tells the compiler Its `genDir` property tells the compiler
to store the compiled output files in a new `aot` folder. 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. 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. Metadata files are not necessary when targeting TypeScript files, so there is no reason to include them.
:marked :marked
***Component-relative Template URLS*** ***Component-relative template URLS***
The AOT compiler requires that `@Component` URLS for external templates and css files be _component-relative_. 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. 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. For example, an `'app.component.html'` URL means that the template file is a sibling of its companion `app.component.ts` file.
While JIT app URLs are more flexible, stick with _component-relative_ URLs for compatibility with AOT compilation. While JIT app URLs are more flexible, stick with _component-relative_ URLs for compatibility with AOT compilation.
:marked :marked
### Compiling the application ***Compiling the application***
Initiate AOT compilation from the command line using the previously installed `ngc` compiler by executing: Initiate AOT compilation from the command line using the previously installed `ngc` compiler by executing:
code-example(language="none" class="code-shell"). code-example(language="none" class="code-shell").
@ -154,7 +165,8 @@ code-example(language="none" class="code-shell").
:marked :marked
`ngc` expects the `-p` switch to point to a `tsconfig.json` file or a folder containing a `tsconfig.json` file. `ngc` expects the `-p` switch to point to a `tsconfig.json` file or a folder containing a `tsconfig.json` file.
After `ngc` completes, look for a collection of _NgFactory_ files in the `aot` folder (the folder specified as `genDir` in `tsconfig-aot.json`). 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. 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 Each component factory creates an instance of the component at runtime by combining the original class file
@ -162,8 +174,8 @@ code-example(language="none" class="code-shell").
Note that the original component class is still referenced internally by the generated factory. Note that the original component class is still referenced internally by the generated factory.
.l-sub-section .l-sub-section
:marked :marked
The curious can open the `aot/app.component.ngfactory.ts` to see the original Angular template syntax The curious can open `aot/app.component.ngfactory.ts` to see the original Angular template syntax
in its intermediate, compiled-to-TypeScript form. compiled to TypeScript, its intermediate form.
JIT compilation generates these same _NgFactories_ in memory where they are largely invisible. JIT compilation generates these same _NgFactories_ in memory where they are largely invisible.
AOT compilation reveals them as separate, physical files. AOT compilation reveals them as separate, physical files.
@ -178,7 +190,7 @@ a#bootstrap
:marked :marked
## Bootstrap ## Bootstrap
The AOT path changes application bootstrapping. The AOT approach changes application bootstrapping.
Instead of bootstrapping `AppModule`, you bootstrap the application with the generated module factory, `AppModuleNgFactory`. Instead of bootstrapping `AppModule`, you bootstrap the application with the generated module factory, `AppModuleNgFactory`.
@ -200,44 +212,46 @@ a#bootstrap
) )
:marked :marked
Be sure to recompile with `ngc`! Be sure to [recompile](#compiling-aot) with `ngc`!
a#tree-shaking a#tree-shaking
:marked :marked
## Tree Shaking ## Tree shaking
AOT compilation sets the stage for further optimization through a process called _Tree Shaking_. 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 A tree shaker walks the dependency graph, top to bottom, and _shakes out_ unused code like
dead needles in a Christmas tree. dead leaves in a tree.
Tree Shaking can greatly reduce the downloaded size of the application Tree shaking can greatly reduce the downloaded size of the application
by removing unused portions of both source and library code. 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. 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. 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. 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 and AOT compilation are separate steps.
Tree Shaking can only target JavaScript code. Tree shaking can only target JavaScript code.
AOT compilation converts more of the application to JavaScript, AOT compilation converts more of the application to JavaScript,
which in turn makes more of the application "Tree Shakable". which in turn makes more of the application "tree shakable".
a#rollup
:marked
### Rollup ### Rollup
This cookbook illustrates a Tree Shaking utility called _Rollup_. This cookbook illustrates a tree shaking utility called _Rollup_.
Rollup statically analyzes the application by following the trail of `import` and `export` statements. 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. It produces a final code _bundle_ that excludes code that is exported, but never imported.
Rollup can only Tree Shake `ES2015` modules which have `import` and `export` statements. Rollup can only tree shake `ES2015` modules which have `import` and `export` statements.
.l-sub-section .l-sub-section
:marked :marked
Recall that `tsconfig-aot.json` is configured to produce `ES2015` modules. 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`. 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. What matters is that the code uses ES `import` and `export` statements rather than `require` statements.
:marked :marked
Install the Rollup dependencies with this command: In the terminal window, install the Rollup dependencies with this command:
code-example(format='.'). code-example(language="none" class="code-shell").
npm install rollup rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-uglify --save-dev npm install rollup rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-uglify --save-dev
:marked :marked
Next, create a configuration file (`rollup-config.js`) Next, create a configuration file (`rollup-config.js`)
@ -246,35 +260,40 @@ code-example(format='.').
+makeExample('cb-aot-compiler/ts/rollup-config.js', null, 'rollup-config.js')(format='.') +makeExample('cb-aot-compiler/ts/rollup-config.js', null, 'rollup-config.js')(format='.')
:marked :marked
It tells Rollup that the app entry point is `src/app/main.js` . 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. 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. It overrides the default `onwarn` method in order to skip annoying messages about the AOT compiler's use of the `this` keyword.
Then there are plugins. The next section covers the plugins in more depth.
a#rollup-plugins
:marked :marked
### Rollup Plugins ### Rollup Plugins
Optional plugins filter and transform the Rollup inputs and output. Optional plugins filter and transform the Rollup inputs and output.
*RxJS* *RxJS*
Rollup expects application source code to use `ES2015` modules. Rollup expects application source code to use `ES2015` modules.
Not all external dependencies are published as `ES2015` modules. Not all external dependencies are published as `ES2015` modules.
In fact, most are not. Many of them are published as _CommonJS_ modules. In fact, most are not. Many of them are published as _CommonJS_ modules.
The _RxJs_ observable library is an essential Angular dependency published as an ES5 JavaScript _CommonJS_ module. The _RxJs_ Observable library is an essential Angular dependency published as an ES5 JavaScript _CommonJS_ module.
Luckily there is a Rollup plugin that modifies _RxJs_ Luckily, there is a Rollup plugin that modifies _RxJs_
to use the ES `import` and `export` statements that Rollup requires. to use the ES `import` and `export` statements that Rollup requires.
Rollup then preserves in the final bundle the parts of `RxJS` referenced by the application. 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`:
+makeExample('cb-aot-compiler/ts/rollup-config.js','commonjs','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.') +makeExample('cb-aot-compiler/ts/rollup-config.js','commonjs','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.')
:marked :marked
*Minification* *Minification*
Rollup Tree Shaking reduces code size considerably. Minification makes it smaller still. 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. This cookbook relies on the _uglify_ Rollup plugin to minify and mangle the code.
Add the following to the `plugins` !{_array}:
+makeExample('cb-aot-compiler/ts/rollup-config.js','uglify','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.') +makeExample('cb-aot-compiler/ts/rollup-config.js','uglify','rollup-config.js (CommonJs to ES2015 Plugin)')(format='.')
@ -283,25 +302,26 @@ code-example(format='.').
In a production setting, you would also enable gzip on the web server to compress 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. the code into an even smaller package going over the wire.
a#run-rollup
:marked :marked
### Run Rollup ### Run Rollup
Execute the Rollup process with this command: Execute the Rollup process with this command:
code-example(format='.'). code-example(language="none" class="code-shell").
node_modules/.bin/rollup -c rollup-config.js node_modules/.bin/rollup -c rollup-config.js
.l-sub-section .l-sub-section
:marked :marked
Windows users should surround the `rollup` command in double quotes: Windows users should surround the `rollup` command in double quotes:
code-example(format='.'). code-example(language="none" class="code-shell").
"node_modules/.bin/rollup" -c rollup-config.js "node_modules/.bin/rollup" -c rollup-config.js
:marked :marked
a#load a#load
.l-main-section .l-main-section
:marked :marked
## Load the Bundle ## Load the bundle
Loading the generated application bundle does not require a module loader like SystemJS. Loading the generated application bundle does not require a module loader like SystemJS.
Remove the scripts that concern SystemJS. Remove the scripts that concern SystemJS.
Instead, load the bundle file using a single `script` tag **_after_** the `</body>` tag: Instead, load the bundle file using a single `<script>` tag **_after_** the `</body>` tag:
+makeExample('cb-aot-compiler/ts/src/index.html','bundle','index.html (load bundle)')(format='.') +makeExample('cb-aot-compiler/ts/src/index.html','bundle','index.html (load bundle)')(format='.')
@ -311,7 +331,7 @@ a#serve
## Serve the app ## Serve the app
You'll need a web server to host the application. You'll need a web server to host the application.
Use the same _Lite Server_ employed elsewhere in the documentation: Use the same `lite-server` employed elsewhere in the documentation:
code-example(language="none" class="code-shell"). code-example(language="none" class="code-shell").
npm run lite npm run lite
:marked :marked
@ -320,7 +340,7 @@ code-example(language="none" class="code-shell").
a#source-code a#source-code
.l-main-section .l-main-section
:marked :marked
## AOT QuickStart Source Code ## AOT QuickStart source code
Here's the pertinent source code: Here's the pertinent source code:
+makeTabs( +makeTabs(
@ -356,7 +376,7 @@ code-example(language="none" class="code-shell").
a#run-jit a#run-jit
:marked :marked
### And JIT too! ### Develop JIT along with AOT
AOT compilation and rollup together take several seconds. AOT compilation and rollup together take several seconds.
You may be able to develop iteratively a little faster with SystemJS and JIT. You may be able to develop iteratively a little faster with SystemJS and JIT.
@ -369,22 +389,24 @@ a#run-jit
:marked :marked
Notice the slight change to the `system.import` which now specifies `src/app/main-jit`. 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](#bootstrap) That's the JIT version of the bootstrap file that we preserved [above](#bootstrap).
:marked :marked
Open a _different_ terminal window and enter. Open a _different_ terminal window and enter `npm start`.
code-example(language="none" class="code-shell"). code-example(language="none" class="code-shell").
npm start npm start
:marked :marked
That compiles the app with JIT and launches the server. That compiles the app with JIT and launches the server.
The server loads `index.html` which is still the AOT version (confirm in the browser console). 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 (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. Develop as usual.
The server and TypeScript compiler are in "watch mode" so your changes are reflected immediately in the browser. The server and TypeScript compiler are in "watch mode" so your changes are reflected immediately in the browser.
To see those changes in AOT, switch to the original terminal and re-run `npm run build: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 back-button to the AOT version in the (default) `index.html`. When it finishes, go back to the browser and use the back button to
return to the AOT version in the default `index.html`.
Now you can develop JIT and AOT, side-by-side. Now you can develop JIT and AOT, side-by-side.
@ -394,13 +416,15 @@ a#toh
:marked :marked
## Tour of Heroes ## Tour of Heroes
The sample above is a trivial variation of the QuickStart app. 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 In this section you apply what you've learned about AOT compilation and tree shaking
to an app with more substance, the tutorial [_Tour of Heroes_](../tutorial/toh-pt6.html). to an app with more substance, the [_Tour of Heroes_](../tutorial/toh-pt6.html) application.
a#jit-dev-aot-prod
:marked
### JIT in development, AOT in production ### JIT in development, AOT in production
Today AOT compilation and Tree Shaking take more time than is practical for development. That will change soon. 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. 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. Fortunately, the source code can be compiled either way without change _if_ you account for a few key differences.
@ -444,7 +468,7 @@ a#toh
***TypeScript configuration*** ***TypeScript configuration***
JIT-compiled applications transpile to `commonjs` modules. JIT-compiled applications transpile to `commonjs` modules.
AOT-compiled applications transpile to _ES2015_/_ES6_ modules to facilitate Tree Shaking. AOT-compiled applications transpile to _ES2015_/_ES6_ modules to facilitate tree shaking.
AOT requires its own TypeScript configuration settings as well. AOT requires its own TypeScript configuration settings as well.
You'll need separate TypeScript configuration files such as these: You'll need separate TypeScript configuration files such as these:
@ -467,14 +491,15 @@ a#toh
In a more typical project, `node_modules` would be a sibling of `tsconfig-aot.json` In a more typical project, `node_modules` would be a sibling of `tsconfig-aot.json`
and `"typeRoots"` would be set to `"node_modules/@types/"`. and `"typeRoots"` would be set to `"node_modules/@types/"`.
Edit your `tsconfig-aot.json` to fit your project's file structure. Edit your `tsconfig-aot.json` to fit your project's file structure.
a#shaking
:marked :marked
### Tree Shaking ### Tree shaking
Rollup does the Tree Shaking as before. Rollup does the tree shaking as before.
+makeExample('toh-6/ts/rollup-config.js',null,'rollup-config.js')(format='.') +makeExample('toh-6/ts/rollup-config.js',null,'rollup-config.js')(format='.')
a#running-app
:marked :marked
### Running the application ### Running the application
@ -513,10 +538,11 @@ code-example(language="none" class="code-shell").
:marked :marked
You won't do that again until there are updates to `zone.js` or the `core-js` shim for old browsers. You won't do that again until there are updates to `zone.js` or the `core-js` shim for old browsers.
:marked :marked
Now AOT-compile the app and launch it with the `lite` server: Now AOT-compile the app and launch it with the `lite-server`:
code-example(language="none" class="code-shell"). code-example(language="none" class="code-shell").
npm run build:aot && npm run serve:aot npm run build:aot && npm run serve:aot
a#inspect-bundle
:marked :marked
### Inspect the Bundle ### Inspect the Bundle