Angular2 CLI, Lazy Loading, and AOT Compilation
Lazy-loading of routes is directly supported in the current Angular platform and they are very easy to organize with NgModules. Additional performance benefits may be realized from the platform by employing AOT (Ahead Of Time) compilation of templates. In short, compilation refers to the process of transforming a string template into a sequence of calls to JS methods that modify the DOM. If you want a more detailed introduction to AOT compilation in Angular, then this is the one of the best blog posts I have found on the topic.
JIT compilation, which is the norm for most examples posted online, is a process by which the compiler is shipped over the wire and templates are compiled inside the browser. While we are practically guaranteed a performance boost from AOT compilation, what about file size and load times? On the one hand, we gain from the fact that the compiler (which is a very large chunk of the platform) does not have to be sent over the wire. On the other hand, we lose from transforming the templates from very compact (and easily compressed) strings, to TS (and then ultimately into JS) factories. Now, the Angular team has done a great job of making the template factories VM friendly, however, this comes at the cost of more verbose code.
So, a general expectation is a performance gain and some file size reduction from the initial load of the platform and eagerly loaded routes. We also expect the file size of lazily-loaded routes to increase, but not by an uncomfortable amount. This should be offset by an increase in performance (i.e. reduced scripting and render time).
Let’s take a look at the entire process for the Three Stooges (lazy-loaded) micro-application. In the past, AOT compilation was a two-step process; compile the modules with ngc and then boot from the relevant app factory. This means either forking a separate project or adding *.jit.ts and *.aot.ts files to the project and then switching as needed to go back and forth from JIT to AOT compilation.
I do find generation of the factory files to be instructive and like to have it available, as illustrated by the built-aot command in my package.json.
Thankfully, the team working on the Angular2 CLI has done a phenomenal job of incorporating AOT compilation into existing projects with minimal source-code modification. I only had to make two changes to get the code working in production within the CLI. First, I removed template bindings to private class variables and methods in the app module. The former was accomplished by adding an accessor and then binding to the public accessor method. The latter was accomplished by using the /** @internal */ annotation and slight mods to the tsconfig.json. This does a fair job of accomplishing the required task while maintaining class encapsulation.
I also had to modify the routes for static analysis, which is a bit cumbersome if the CLI is installed globally. This is the hack.
There are a couple ways around this. I’m going to wait until the CLI is in production and then follow documented best practices at that time. I will then modify and release the source of this project to Github.
For the mean time, it is now a matter of using beta 24 of the CLI and the command
ng-build –aot –prod –base-href /stoogesaot/
There is no need to create a special boot module.
I deployed the code to my server and then ran some tests (clearing browser cache after each test). Keep in mind that these results were on my 2009 Macbook Pro. YMMV.
First, note that the file size of main.<hash>.bundle.gz went from 196K to 112K as a result of AOT compilation. For most environments with a big pipe, the difference in load time from the two would be considered minimal. For a SaaS environment where applications are served to hundreds of thousands or millions of users daily, the cumulative savings could be considered quite significant.
Here are the results of the initial load for the JIT application (two eagerly loaded routes). This is the minimum to put the application in front of the user.
(the idle time is delay from me stopping the recording
and the equivalent from the AOT-compiled version.
Load times between the two are pretty much within noise. Note the rather dramatic reduction in scripting and rendering time. For older machines (like my 2009 MBP) and mobile devices, this is significant.
Next, I recorded the process of clicking quickly between the remaining routes, ‘history’, ‘bios’, and ‘partial episode list’. I did note that file sizes for these chunks increased, but only by a modest margin and I could not detect any significant effect on load time between multiple runs of the two applications. Here are the performance results:
These templates (particularly the partial episode list) are more complex than those for the eagerly-loaded components. They require more effort to compile in-browser, so the performance difference from AOT compilation is pretty dramatic.
This really illustrates the beauty of the new Angular ecosystem. We have lazy-loading directly and efficiently supported in the platform and the ability to compile templates offline. The CLI is coming along very well with a 1.0 release expected soon. Rumor has it that the CLI may be eventually incorporated into WebStorm, which would provide a rich GUI for application development in Angular.
I feel confident in stating that 2017 will be the year of Angular.