Blog
Contact

What’s the state of Angular Universal and AOT?

Michael Prentice
Dec 20, 2016

I’ve spent a good deal of the last week looking at Angular Universal and Ahead of Time (AOT) compilation. Until now, I had been waiting to dig into them because it was clear they were still in the experimental/alpha stages.

However, I decided that I want to update the DevIntent website from Angular 1.5.7 to the latest version of Angular over the Christmas break!

Since this is a marketing website, I need it to be super fast, especially on mobile, and have excellent SEO! I’m also using this as a testing ground for some work on an upcoming client project.

Current website status

Times listed for No throttling / Regular 3G.

  • 36 requests

  • 1.2 MB

  • 459ms / 3760ms DOMContentLoaded

  • 920ms / 13850ms Load

This is fairly typical of a smaller Angular 1.5.x application that is using a few libraries (Angular Material, Font Awesome, etc.) and is hosted on Firebase Hosting. It’s fine on desktop with broadband internet, but not good enough on a mobile 3G device.

Bust out the Angular-CLI

My first choice is generally the Angular-CLI because I believe in what they are doing to make Angular more approachable to new users. I use it as much as I can so that I can provide feedback, open bug reports, and even submit PRs.

The first step was to see if Universal and AOT was working yet in the CLI. I found that they were both working, just not together. The Angular-CLI does not support Angular Universal. It does support AOT, but there are around 63 open issues related to AOT.

There is a separate fork for a version of the CLI that supports Universal, but it doesn’t support AOT. It appears that they created this fork because the Angular-CLI team didn’t have the resources to support Universal.

Grab one of the hot seed projects

From the latest presentations that I’ve seen at conferences, I knew that there were a number of great Angular seeds out there.

The first one I looked at was angular-seed from Minko Gechev. It is really well-known, has 3,344+ stars, and supports AOT, Docker, etc. Unfortunately it doesn’t support Angular Universal or Webpack. After using Webpack with the Angular-CLI, I decided that I don’t want to use another bundler in my non-Angular-CLI projects.

I checked into Universal Starter from the Angular Universal team. It uses Webpack and has basic support for AOT. But it is really minimal and doesn’t include support for SASS/SCSS. I decided that I might come back to it.

Angular2-Webpack-Starter from AngularClass was another that caught my eye since it obviously supported Webpack, but it also had a lot of the other stuff ready to go. This included Router, HTTP, Forms, Hot Module Reload (HMR), Async/Lazy Routes, Material Design, and tests! It also has a great list of guides and tips like setting up SASS/SCSS. Unfortunately, it doesn’t yet support AOT compilation or Angular Universal.

Finally picking a seed

I settled on giving Anthony’s (qdouble) fork of angular2-webpack-starter a shot. It combines all the great stuff from the original angular2-webpack-starter with support for AOT and Universal! One thing I quickly noticed was that the project is stuck on an older version (v2.1) of Angular (latest is v2.3).

After a lot of research, it became obvious that changes to Angular core in v2.2 and v2.3 broke Angular Universal. Apparently the Angular core team has realized that this is a problem.

Because of this, they decided a couple weeks ago to move key pieces of Angular Universal into the Angular core. Unfortunately, a number of key people have been on vacation since this decision was made. So we’re stuck in this state where using Angular Universal means staying on Angular v2.1.

This also means that support for Angular Universal in a new version of Angular will have to wait for at least v4 which just entered beta.0.

Measuring the benefits

The main purpose of the chosen seed/starter is performance (i.e. Angular Universal, Webpack 2, and AOT compilation). I checked the README for some stats for nerds (i.e. perf goodies) but sadly found none. Thus, I set to gathering some performance stats on the current build of the starter.

  • Times listed for No throttling / Regular 3G.

Dev Mode

npm start

  • 29 requests
  • 5.8 MB
  • 1300ms / 66000ms DOMContentLoaded
  • 1350ms / 66000ms load

Universal

npm run universal

  • 16 requests
  • 363 KB
  • 928ms / 4480ms DOMContentLoaded
  • 973ms / 5020ms load

AOT mode

npm run compile + npm run prodserver

  • 16 requests
  • 301 KB
  • 432ms / 3530ms DOMContentLoaded
  • 495ms / 3930ms load

Universal + AOT

npm run universal:aot or npm run compile:universal+ npm run prodserver

  • 16 requests
  • 392 KB
  • 494ms / 4410ms DOMContentLoaded
  • 535ms / 5030ms load

Tests Ran on

  • Chrome 57.0.2951.0 canary (64-bit)

  • Cache disabled

  • MacBook Pro (Retina, 15-inch, Mid 2014)

  • 2.8 GHz Intel Core i7

  • 16 GB 1600 MHz DDR3

  • OS X El Capitán 10.11.6 (15G1212)

  • NodeJS 6.7.0

Analysis

Now I know that Universal and AOT are still undergoing a lot of breaking changes and needed improvements, but I found it a little odd that the combination (Universal + AOT) was slower than the build with AOT alone.

Since the angular-cli isn’t supporting Universal + AOT yet, I was looking to find a solid seed for Universal, AOT, Webpack 2, Material 2, SCSS, etc. and this one seems to be the closest that I can find which isn’t significantly broken.

With the numbers above, I was wondering if I should even bother with Universal at this point. Then I thought, well Universal should help get that initial paint a lot faster. But on any network with 3G+, the time between initial paint and interactivity is under 50ms and isn’t even noticeable. If I drop down to Regular 2G, then I do see the initial DOM loaded for about 2s before JavaScript takes control and the page is re-rendered and fully ready for interaction.

Perhaps part of this issue is that the app doesn’t have an appShell (it’s just a white “Loading…” page), but either way I’m not sure I should go to the extra trouble of running a NodeJS server for Universal at this point. I could just host a client-only app in Firebase Hosting using their Edge CDN w/ AOT and maybe that would be even faster than the numbers above.

SEO is something that I really need, but I’m not convinced that Universal will work properly here and deliver the desired results. There’s some possible hacks discussed in the universal-starter project where the meta tags can be manipulated. However, Angular’s new Meta Service just landed in Angular v4-beta.0 thanks to Dzmitry Shylovich! Unfortunately Universal isn’t compatible with the latest versions of Angular yet.

TL;DR

It appears that I should just stick with AOT compilation and wait until Universal gets better support from the Angular core team (in progress, no ETA).

Thankfully the chosen starter makes that a well-supported option in addition to supporting these configurations used in the benchmarks above. This is what enabled the ability to gather these numbers using different approaches, while keeping the code-base exactly the same. Thanks to Anthony for the hard work on this starter and for sharing what’s been working for him on his projects!

Michael Prentice
Dec 20, 2016