What’s the state of Angular Universal and AOT?
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.
Found a number of seeds w/ #AngularUniversal support, but they all seem to be broken (#ngMaterial2 / #AOT) or stuck on #Angular 2.1.
— Michael Prentice (@Splaktar) December 15, 2016
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.
But I just saw this https://t.co/eJtyJEVZrw which mentions that #Universal is moving to angular core. #ngMaterial2 is waiting on the move.
— Michael Prentice (@Splaktar) December 14, 2016
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
.
Angular 4.0.0-beta.0 is out. You're wondering why it's Angular 4? Read about the reasoning behind here: https://t.co/9AhXTMnlXc #angular
— Juri Strumpflohner (@juristr) December 15, 2016
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!