This cookbook describes how to radically improve performance by compiling Ahead of Time (AOT) during a build process.
Table of Contents
- Ahead-of-Time vs Just-in-Time
- Compile with AOT
- Tree Shaking
- Load the bundle
- Serve the app
- Workflow and convenience script
- Source Code
- Tour of Heroes
Watch compiler author Tobias Bosch explain the Angular Compiler at AngularConnect 2016.
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.
Compilation can uncover many component-template binding errors. JIT compilation discovers them at runtime which is later than we'd like.
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.
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. 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.
Why do AOT compilation?
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.
Fewer asynchronous requests
Smaller Angular framework download size
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
The AOT compiler detects and reports template binding errors during the build step before users can see them.
Compile with AOT
Prepare for offline compilation
Take the Setup as a starting point.
A few minor changes to the lone
app.component lead to these two class and html files:
Install a few new npm dependencies with the following command:
You will run the
ngc compiler provided in the
@angular/compiler-cli npm package
instead of the TypeScript compiler (
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.
Copy the original
src/tsconfig.json to a file called
tsconfig-aot.json (on the project root),
then modify it to look as follows.
compilerOptions section is unchanged except for one property.
This is important as explained later in the Tree Shaking section.
What's really new is the
ngc section at the bottom called
"genDir" property tells the compiler
to store the compiled output files in a new
"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.
Component-relative Template URLS
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
While JIT app URLs are more flexible, stick with component-relative URLs for compatibility with AOT compilation.
JIT-compiled applications that use the SystemJS loader and component-relative URLs must set the
@Component.moduleId property to
module object is undefined when an AOT-compiled app runs.
The app fails with a null reference error unless you assign a global
module value in the
index.html like this:
Setting a global
module is a temporary expedient.
Compiling the application
Initiate AOT compilation from the command line using the previously installed
ngc compiler by executing:
Windows users should surround the
ngc command in double quotes:
ngc expects the
-p switch to point to a
tsconfig.json file or a folder containing a
ngc completes, look for a collection of NgFactory files in the
aot folder (the folder specified as
The curious can open the
aot/app.component.ngfactory.ts to see the original Angular template syntax
in its intermediate, compiled-to-TypeScript form.
JIT compilation generates these same NgFactories in memory where they are largely invisible. AOT compilation reveals them as separate, physical files.
Do not edit the NgFactories! Re-compilation replaces these files and all edits will be lost.
The AOT path changes application bootstrapping.
Instead of bootstrapping
AppModule, you bootstrap the application with the generated module factory,
Make a copy of
main.ts and name it
This is the JIT version; set it aside as you may need it later.
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
Here is AOT bootstrap in
main.ts next to the original JIT version:
Be sure to recompile with
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 needles in a Christmas 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
There is no reason to download Forms-related Angular code and tree shaking ensures that you don't.
This cookbook illustrates a Tree Shaking utility called Rollup.
Rollup statically analyzes the application by following the trail of
It produces a final code bundle that excludes code that is exported, but never imported.
Rollup can only Tree Shake
ES2015 modules which have
tsconfig-aot.json is configured to produce
It's not important that the code itself be written with
ES2015 syntax such as
What matters is that the code uses ES
export statements rather than
Install the Rollup dependencies with this command:
Next, create a configuration file (
in the project root directory to tell Rollup how to process the application.
The cookbook configuration file looks like this.
It tells Rollup that the app entry point is
dest attribute tells Rollup to create a bundle called
build.js in the
It overrides the default
onwarn method in order to skip annoying messages about the AOT compiler's use of the
Then there are plugins.
Optional plugins filter and transform the Rollup inputs and output.
Rollup expects application source code to use
Not all external dependencies are published as
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
export statements that Rollup requires.
Rollup then preserves in the final bundle the parts of
RxJS referenced by the application.
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.
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.
Execute the Rollup process with this command:
Windows users should surround the
rollup command in double quotes:
Load the Bundle
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
Serve the app
You'll need a web server to host the application. Use the same Lite Server employed elsewhere in the documentation:
The server starts, launches a browser, and the app should appear.
AOT QuickStart Source Code
Here's the pertinent source code:
Workflow and convenience script
You'll rebuild the AOT version of the application every time you make a change. Those npm commands are long and difficult to remember.
Add the following npm convenience script to the
package.json so you can compile and rollup in one command.
Open a terminal window and try it.
And JIT too!
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.
- Make a copy of
index.htmland call it
- Delete the script at the bottom of
- Restore the SystemJS scripts like this:
Notice the slight change to the
system.import which now specifies
That's the JIT version of the bootstrap file that we preserved above
Open a different terminal window and enter.
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).
Change the address bar to
index-jit.html and it loads the JIT version (confirm 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.
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)
Now you can develop JIT and AOT, side-by-side.
Tour of Heroes
The sample above is a trivial variation of the QuickStart app. 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.
JIT in development, AOT in production
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.
The JIT and AOT apps require their own
index.html files because they setup and launch so differently.
Here they are for comparison:
The JIT version relies on
SystemJS to load individual modules.
Its scripts appear in its
The AOT version loads the entire application in a single script,
It does not need
SystemJS, so that script is absent from its
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
AOT-compiled applications transpile to ES2015/ES6 modules to facilitate Tree Shaking.
AOT requires its own TypeScript configuration settings as well.
You'll need separate TypeScript configuration files such as these:
In the file structure of this particular sample project,
node_modules folder happens to be two levels up from the project root.
"typeRoots" must be set to
In a more typical project,
node_modules would be a sibling of
"typeRoots" would be set to
tsconfig-aot.json to fit your project's file structure.
Rollup does the Tree Shaking as before.
Running the application
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.
The Tour of Heroes source code is in the
Run the JIT-compiled app with
npm start as for all other JIT examples.
Compiling with AOT presupposes certain supporting files, most of them discussed above.
scripts section of the
package.json with these npm scripts:
Copy the AOT distribution files into the
/aot folder with the node script:
You won't do that again until there are updates to
zone.js or the
core-js shim for old browsers.
Now AOT-compile the app and launch it with the
Inspect the Bundle
Run the following command to generate the map.
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.
Here's the map for Tour of Heroes.