Compiling templates ahead of time gives an immediate boost to initial load times, since the browser no longer has to parse and compile them at runtime. Combined with tree shaking, which strips out unused modules, the overall payload shrinks accordingly. However, there are a handful of less obvious consequences that surface once you start relying on these features in real-world projects. Below are five findings from our own work with AOT and tree shaking.
This piece grew out of extended discussions with Carmen Popoviciu. She flagged the quirks around AOT and tree shaking, helped trace them back to their roots, and gave the whole text a careful review. Many thanks for that.
The expected one: EcmaScript Modules
To make tree shaking effective, your source code needs to be written using EcmaScript 2015, and specifically the module system introduced there. Those import and export statements enable static analysis of the code, which is exactly what allows the tooling to pick out unused pieces.
It’s also feasible to stick with EcmaScript 5 syntax while still leveraging the module format, which broadens compatibility with tools like webpack 2 that didn’t yet support the full ES2015 language spec but could still trace import/export for dead-code elimination back then.
The Angular packages themselves are shipped using that blended approach, plus extra UMD bundles. As you’ll see in the next part, that choice brings its own complications because analyzing ES5 code for removal-safe unused exports is far from trivial.
What tree shaking still can’t do
Demo projects make tree shaking look great, but in practice the savings are often below the theoretical maximum. The reasoning behind this is that tools such as Rollup or webpack 2 have to stay conservative: when they can’t be certain a piece of code is side-effect free, they keep it. Anything that might mutate a global or otherwise change state outside the module has to remain in the bundle.
One prominent offender is the Object.defineProperty call, since it modifies the object it receives. And because this is exactly what transpilers emit when converting class members down to ES5, large portions of Angular itself and libraries like Angular Material end up being affected.
You can see this in practice by switching on the warning output in your webpack configuration. UglifyJS will then log every chunk that stays in the bundle for exactly that reason:
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: true
},
output: {
comments: true
},
sourceMap: false
}),
The effect becomes even more tangible if you import something like Angular Material into your AppModule and don’t use it anywhere. Intuition says tree shaking should drop the whole thing, but because of the issue described above, the bundle actually grows.
Both the webpack and the rollup issue trackers carry longer discussions about exactly this problem.
Smaller bundles? Not always
It feels safe to assume that AOT and tree shaking inevitably lead to smaller bundles. Tree shaking does generally cut down on size, but AOT moves template compilation from runtime to build time, meaning the emitted JavaScript gets larger than the original templates were. For small apps this barely matters, especially since you no longer need to ship the JIT compiler. But once the number of templates crosses a certain threshold, the extra generated code ends up outweighing what you saved by dropping JIT.
Update: Angular 4 improved this situation considerably.
SASS and friends
Working with the AOT compiler in combination with preprocessors like SASS or SCSS can run into errors right away. The compiler has no built-in understanding of these languages. The webpack plugin described in this write-up handles the case seamlessly when a matching webpack loader is present.
Libraries have to do their part
Tree shaking and AOT only work for library code if the library itself is built to support them. That means more than just exposing ES modules — the library also has to ship metadata, particularly the kind Angular’s AOT compiler needs.
Minko Gechev has written an excellent article on exactly what that entails, including a practical checklist of everything a library author should keep in mind.
