Here’s a high level summary of the results.

File Uncompressed Gzipped Brotli Compression Closure Compiled (Advanced Optimizations, Uncompressed) Closure Compiled (Gzipped)
inline.bundle.js 4K 918B 4K 1.3K 728B
main.bundle.js 464K 123K 104K 385K 110K
polyfills.bundle.js 144K 48K 44K 129K 46K
styles.bundle.css 52K 8K 8K 52K (Closure Compiler JS only) 8K
Total 664K 179.9K 160K 567.3K 164.7K

Set a Baseline

First, set a baseline for what the current app size is so you can measure improvements. You can use the du (disk usage) command to accomplish this. The code below shows how to do it. -h makes it human readable and -d 0 means that it will only look at the current directory level (it won’t recursively list files in the dir and subdirs).

$ du -d 0 h dist
1.1M	dist
$ du -h dist/*
8.0K	dist/3rdpartylicenses.txt
140K	dist/MaterialIcons-Regular.eot
128K	dist/MaterialIcons-Regular.ttf
 60K	dist/MaterialIcons-Regular.woff
 44K	dist/MaterialIcons-Regular.woff2
 52K	dist/assets
4.0K	dist/index.html
4.0K	dist/inline.bundle.js
464K	dist/main.bundle.js
144K	dist/polyfills.bundle.js
 52K	dist/styles.bundle.css

To simplify analysis I’ll focus on these 4 files because these are the ones we have the most control over:

4.0K	dist/inline.bundle.js
464K	dist/main.bundle.js
144K	dist/polyfills.bundle.js
 52K	dist/styles.bundle.css

Total = 664K

Serving 664K to everyone who visits our site is lot. We can do much better, but how?

Tip 0: Target Production

Run ng build --prod. This flag enables several other optimization features such as AOT which pre-compiles Angular component templates, bundles together Javscript files into main.bundle.js, minifies by removing whitespace and comments, and uglifies by replacing variable names with shorter, cryptic names, and eliminates dead code.

It also helps to use the --build-optimizer flag. This flag will exclude modules that are imported but aren’t actually used in the application. This is sometimes called “tree shaking” and can lead to significant decrease in app size.

Tip 1: Compress, Compress, Compress

Gzip is a widely used compression algorithm that can dramatically reduce app size. I saw a ~73% reduction in app size with this approach.

918B	dist/inline.bundle.js
123K	dist/main.bundle.js
 48K	dist/polyfills.bundle.js
  8K	dist/styles.bundle.css
  
-> total = ~180K

Brotli compression is another option that is more effective than Gzip in many cases but not as widely used and supported.

Here is a table that shows how Brotli compression is supported across major browsers, from caniuse.com. Brotli Compression Browser Support Table

If you’ve never heard of Brotli before it’s worth a try. It’s easy to install on macOS with Homebrew. Run brew install brotli and for i in dist/*; do brotli $i; done to Brotli compress files in dist/. Sure enough, the results are better than gzip. I saw a ~76% reduction with this approach.

4.0K    dist/inline.bundle.js.br
104K    dist/main.bundle.js.br
 44K    dist/polyfills.bundle.js.br
8.0K    dist/styles.bundle.css.br

-> total 160K

Tip 2: Check for Huge Imports

Make sure you don’t import entire modules like @angular/core, but just the submodules that are required. Other libraries to be careful of are @angular/common and rxjs.

For example, you may have something like this in your code: import 'rxjs';. Ask yourself if you really need to import the full RxJS library or if you just need one module from it. For example, if you only need Observable you can reduce the app size by saying import {Observable} from 'rxjs/Observable'.

Tip 3: Update Angular CLI

On Jan 2, 2018 the owner of the angular github repo, IgorMinar, said that “most of the optimizations are on by default in the latest cli release.” You may be missing out on some optimizations just because you’re not running the latest version of angular-cli.

Tip 4: Use source-map-explorer to pinpoint problems

source-map-explorer let’s you see which files are taking up the most space. Install the source-map-explorer NPM package then run it like this: source-map-explorer dist/polyfills.bundle.js dist/polyfills.bundle.js.map.

*.js.map files are required for source-map-explorer to work, but they aren’t included by default if you’re compiling with prod as a target. If you want to examine prod compiled assets with the source-map-explorer then include the --sourcemaps argument when building. For example, ng build --prod --sourcemaps.

The view will look like this: Angular 4 Polyfills zone.js

You may notice that zone.js takes up a lot of space, this is exactly the kind of insight source-map-explorer can provide for us. However, after more digging it turns out this file is required by Angular itself, so I’ll need to find some other large files to eliminate.

Tip 5: Use Google’s Closure Compiler

Some people have used Google’s Closure Compiler tool to get ~50K builds. YMMV, but it is worth trying this tool to see if it helps. It is free and hosted here.

For my project, the closure compiler did offer some improvements, especially when advanced optimizations were enabled. However, be aware that the advanced optimizations make certain assumptions about the way the code is written and may break your code if those assumptions are violated. More information about these assumptions is available here.

My closure compiler results:

Uncompressed

1.3K dist/inline.bundle.js
385K dist/main.bundle.js
129K dist/polyfills.bundle.js
 52K dist/styles.bundle.scss
 
-> total = 567.3K

Gzipped

728B	dist/inline.bundle.js
110K	dist/main.bundle.js
 46K	dist/polyfills.bundle.js
  8K	dist/styles.bundle.css
  
-> total = ~164.7K

Wrap Up

Thanks for reading! I hope some of these tips have benefited you.