Shrinking Angular Bundles With The Angular Build Optimizer

Shrinking Angular Bundles With The Angular Build Optimizer

Thanks to Filipe Silva who reviewed this article and to Rob Wormald for a lot of insights regarding this technology. Also thanks to Sander Elias who gave important feedback.

Update 2017-11-06: Since Angular CLI 1.5, the Build Optimizer is on by default when doing a production build.

Beginning with version 1.3.0-rc.0, the Angular CLI makes use of the Angular Build Optimizer. This is a nifty tool that transforms the emitted JavaScript code to make tree shaking more efficient. This can result in huge improvements regarding bundle size. In this post I'm describing some simple scenarios that show the potential of this newly introduced tool. If you want to reproduce these scenarios, you find the source code used in my GitHub repository.

Please note that the Angular Build Optimizer was still experimental when writing this. Therefore, it is subject to change. Nevertheless, as shown below, it comes with a high potential for shrinking bundles.

Scenario 1

To demonstrate the power of the Angular Build Optimizer, I'm using a simple Angular Application I've created with the Angular CLI. After scaffolding, I've added the MdButtonModule and the MdCheckboxModule from Angular Material as well as Angular's NoopAnimationsModule and FormsModule:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { MdButtonModule, MdCheckboxModule } from '@angular/material';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { FormsModule } from "@angular/forms";

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    NoopAnimationsModule,
    MdButtonModule,
    MdCheckboxModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Please note that I'm using none of the added modules. I've just added them to find out how good the CLI/ webpack is in combination with the Angular Build Optimizer when it comes to shake them off.

After this, I've created a production build without using the Build Optimizer and another one using it. For the first one, I've leveraged the new --build-optimizer command line option:

ng build --prod --build-optimizer=false

For the first one you can just create a production build, as starting with CLI 1.5 the Build Optimizer is on by default. Before, you needed to use the --build-optimizer flag:

ng build --prod --build-optimizer

The results of this are amazing:

~ 94 KB with and ~ 194 KB without the Optimizer (gzip)

As this figure shows, using the Angular Build Optimizer the bundle size after tree shaking is about the half. This fits to my observations I've written down here some months ago: There are situations that prevent tree shaking implementations from doing their job as well as they can. The Build Optimizer seems to compensate this.

Scenario 2

After this, I've added one component from each of the two included Angular Material modules to find out whether this is influencing tree shaking:

<!-- app.component.html -->

<button md-raised-button (click)="doIt()">Raised button</button>
<md-checkbox class="example-margin" [(ngModel)]="checked">Checked</md-checkbox>

This led to the following results:

117 KB with and 224 KB without the Optimizer

Of course, both bundles are bigger, because now I'm using more parts of the bundles included. But, as before, using the Angular Build Optimizer our Bundles are about half as big.

Scenario 3

Perhaps you are wondering what's the overhead introduced by the two Angular Material modules in the scenarios above. To find this out, I've removed referenced to their Angular Modules and created two more build -- one with and one without the Angular Build Optimizer:

88 KB with and 108 KB without the Optimizer

Compared to Scenario 1 this shows that when using the Build Optimizer, it is possible to shake off most parts of the Material Design Modules when they are imported but not used.

Current Limitations

As mentioned above, when writing this the Angular Build Optimizer was still experimental and therefore subject to change. Currently, it can introduce some issues when transforming the source code. For instance currently source maps are broken when creating a (production) build with the Optimizer. There is also an issue with rxjs operators and console logs. Another issue can arise when using getters with side effects as the optimizer leverages UglifyJS' pure_getters option. This option makes Uglify to remove unused calls to getters and this leads to problems if you depend on their side effects. Please also note, that these issues can also affect included packages you don't have under your control.

Nethertheless, as shown here, the Build Optimizer has a high potential.

Conclusion

The Angular Build Optimizer removed the issues that prevented tree shaking tools to do their job. This can result in a drastic reduction of bundle sizes.