Angular 2 introduces the concept of ahead-of-time compilation (AOT). This is an alternate configuration in which you can run your applications to move some processing time from inside the browser (referred to as just-in-time compilation or JIT) to when you compile your application on the server.
The code, links, and a live example related to this recipe are available at .
AOT compilation is application-agnostic, so you should be able to add this to any existing Angular 2 application with minimal modification.
For the purposes of this example, suppose you have an existing AppModule
inside app/app.module.ts
. You needn't concern yourself with its content since it is irrelevant for the purpose of AOT.
Using AOT means you will compile and bootstrap your application differently. Depending on how it is configured, you will probably want to use sibling files with "aot" added to the name. Bear in mind that this is only for organizational purposes; Angular is not concerned with what you name your files.
Angular requires a new compilation tool ngc
, which is included in the @angular/compiler-cli
package. It will also make use of platformBrowser
to bootstrap, which is provided inside the @angular/platform-browser
package. Install both of these and save the dependency to package.json
:
npm install @angular/compiler-cli @angular/platform-server --save
ngc
will essentially perform a superset of duties of the tsc compilation tool you are accustomed to using. It is wise to provide it with a separate config file, which can be named tsconfig-aot.json
(but you are not beholden to this name).
The file should appear identical to your existing tsconfig.json
file but with the following important modifications:
[tsconfig-aot.json] { "compilerOptions": { (lots of settings here already, only change 'module') "module": "es2015", }, "files": [ "app/app.module.ts", "main.ts" ], "angularCompilerOptions": { "genDir": "aot", "skipMetadataEmit" : true } }
AOT compilation requires your application to be organized in a few specific ways. Nothing should break an existing application, but they're important to do and take note of. If you've been following Angular best practices, they should be a breeze.
First, component definitions must have a moduleId
specified. You will find that components generated with the Angular CLI already have this included. If not, the moduleId
should always be module.id
, as shown here:
@Component({ moduleId: module.id, (lots of other stuff) })
Since the module is undefined when compiling in AOT, you can provide a dummy value in the root template:
<script>window.module = 'aot';</script>
AOT doesn't need the module ID, but it allows you to have a compilation without errors. Note that these steps involving moduleId
may be changed or eliminated entirely in future versions of Angular, as they only exist to allow you to have compatibility between both JIT and AOT compilations.
Next, you'll need to change the path of the templates, both CSS and HTML, to be relative to the component definition file, not application-root-relative. If you are using the convention given by the Angular CLI or the style guide, you are probably already doing this.
ngc
isn't available directly on the command line; you'll need to run the binary file directly. Additionally, since in this example it's not using the tsconfig.json
naming convention, you'll need to point the binary to the location of the alternate config file. Run the following command from the root of your application to execute the compilation:
node_modules/.bin/ngc -p tsconfig-aot.json
This command will output a collection of NgFactory
files with .ngfactory.ts
inside the aot directory, as you specified earlier in the angularCompilerOptions
section of tsconfig-aot.json
.
The compilation is done, but your application doesn't yet know how to use these NgFactory
instances.
Your application will now start off with AppModuleNgFactory
instead of AppModule
. Bootstrap it using platformBrowser
:
[main.ts] import {platformBrowser} from '@angular/platform-browser'; import {AppModuleNgFactory} from 'aot/app/app.module.ngfactory'; platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
With this, you'll be able to run your application normally but this time using precompiled files.
Compiling in Angular is a complex subject, but there are several main points relevant to switching an application to an AOT build process:
@angular/compiler
module, which is quite large. Using AOT means this module no longer needs to be delivered to the browser, which yields substantial bandwidth savings.template
or templateUrl
, and converting them into NgFactory
instances. These instances specify how Angular understands how you defined the template. The conversion to Factory
instances happens both in JIT and AOT compilation; switching to AOT simply means you are doing this processing on the server instead of the client and serving NgFactory
definitions directly instead of uncompiled template strings.NgFactory
definitions that the AOT compilation generates, it is best to do JIT compilation when developing the application and AOT compilation when building and deploying production applications.AOT is cool, but it might not be for everybody. It will very likely reduce the load time and initialization time of your Angular application, but your build process is considerably more involved now. What's more, if you want to use JIT in development but AOT in production, you now have to maintain two versions of three different files: index.html
, main.ts
, and tsconfig.json
. Perhaps this additional build complexity overhead is worth it, but it should certainly be a judgment call based on your development situation.
Angular also supports a separate step of optimization called Tree Shaking. This uses a separate npm library called rollup
. Essentially, this library reads in your entire application (as JS files), figures out what modules are not being used, and cuts them out of the compiled codebase and therefore of the payload that is delivered to the browser. This is analogous to shaking a tree to make the dead branches fall out, hence "tree shaking."
The es2015 module configuration specified earlier in tsconfig-aot.json
was for supporting the rollup library, as it is a requirement for compatibility.
If your application code base is well-maintained, you will most likely see limited benefits of tree shaking since unused imports and the like will be caught when coding. What's more, one could make the argument that tree shaking might be an anti-pattern since it subtly encourages a liberal use of module inclusion by the developer with the knowledge that tree shaking will do the dirty working of cutting out any unused modules. This may then lead to a cluttered code base.
Using tree shaking can be a useful tool when it comes to Angular 2 applications, but its usefulness is in many ways evaporated by keeping a code base tidy.