Drop 20K from your production Angular app

Replacing core-js with a modern reflect-metadata implementation will shave 20K off of your production Angular bundle.

Lets start with generating a new Angular app using the @angular/cli. The version we are using is 6.1.0 .

$ ng --version [ snip] Angular CLI: 6.1.0 Node: 10.7.0 OS: darwin x64 Angular: ... Package Version ------------------------------------------------------ @angular-devkit/architect 0.7.0 @angular-devkit/core 0.7.0 @angular-devkit/schematics 0.7.0 @schematics/angular 0.7.0 @schematics/update 0.7.0 rxjs 6.2.2 typescript 2.7.2

Generate a new app ng-reflection .

$ ng new ng-reflection [ snip] added 1170 packages from 1290 contributors and audited 24195 packages in 38.542s found 13 vulnerabilities ( 9 low, 4 high ) run ` npm audit fix ` to fix them, or ` npm audit ` for details Successfully initialized git.

From within the ng-reflection directory install the @abraham/reflection dependency. (If you're on an older version of npm you'll have to add --save ).

$ npm install @abraham/reflection + @abraham/reflection@0.4.0 added 1 package from 1 contributor and audited 24196 packages in 9.428s found 13 vulnerabilities ( 9 low, 4 high ) run ` npm audit fix ` to fix them, or ` npm audit ` for details

Now let's get our benchmark production build size. ( -- tells npm to pass the following --prod to the binary).

$ npm run build -- --prod > ng-reflection@0.0.0 build /Users/abraham/dev/sandbox/ng-reflection > ng build "--prod" Date: 2018-07-26T15:16:08.577Z Hash: 5195e37ef7e0f497ed61 Time: 23106ms chunk { 0 } runtime.a66f828dca56eeb90e02.js ( runtime ) 1.05 kB [ entry] [ rendered] chunk { 1 } styles.34c57ab7888ec1573f9c.css ( styles ) 0 bytes [ initial] [ rendered] chunk { 2 } polyfills.2f4a59095805af02bd79.js ( polyfills ) 59.6 kB [ initial] [ rendered] chunk { 3 } main.81ad5e275fee95455d5e.js ( main ) 172 kB [ initial] [ rendered]

Note the 59.6 kB size of the "polyfills" bundle.

Let's go into src/polyfills.ts file and replace the core-js dependency with @abraham/reflection.

-import 'core-js/es7/reflect' +import '@abraham/reflection';

Run the production build again to see how this changes the build size.

$ npm run build -- --prod > ng-reflection@0.0.0 build /Users/abraham/dev/sandbox/ng-reflection > ng build "--prod" Date: 2018-07-26T15:19:55.624Z Hash: 73d84e088f5330b2c3c2 Time: 15921ms chunk { 0 } runtime.a66f828dca56eeb90e02.js ( runtime ) 1.05 kB [ entry] [ rendered] chunk { 1 } styles.34c57ab7888ec1573f9c.css ( styles ) 0 bytes [ initial] [ rendered] chunk { 2 } polyfills.ab3b721569e8573cc20c.js ( polyfills ) 39.4 kB [ initial] [ rendered] chunk { 3 } main.81ad5e275fee95455d5e.js ( main ) 172 kB [ initial] [ rendered]

Neat! The "polyfills" bundle is now only 39.4 kB, a savings of 20 kB.

What even is this?

reflect-metadata

Reflect-metadata was a JavaScript decorator proposal that TypeScript experimentally implemented. Note that a different decorators proposal is currently at stage 2 at tc39.

Using decorators you can write encapsulated functionality to be shared among a lot of of code. Sitepoint has a good intro to decorators. Basically they let you define functions like log , immutable , and time which you can apply to classes, functions, and properties.

@ log () @ immutable () class Example { @ time ( ' demo ' ) doSomething () { // } }

core-js

Angular makes use of TypeScript decorators through another metadata implementation, core-js. Core-js is awesome and provides a huge number of other JavaScript polyfills. If you're supporting older browsers and using other core-js polyfills it might make sense to stick with it.

One example is Component that lets us define a class as an Angular Component and configure it's behavior.

@ Component ({ selector : ' app-bank-account ' , inputs : [ ' bankName ' , ' id: account-id ' ], template : ` Bank Name: {{ bankName }} Account Id: {{ id }} ` }) export class BankAccountComponent { bankName : string ; id : string ; // this property is not bound, and won't be automatically updated by Angular normalizedBankName : string ; }

@abraham/reflection

@abraham/reflection is a modern, lightweight, ES module rewrite of reflect-metadata. For most use cases you should be able to replace reflect-metadata or core-js/es7/reflect without issue.

Give @abraham/reflection a try and see if it reduces your bundle size. Keep in mind that it's a new library and might have some kinks to work out. It is running in production on pwa.ng though. If you have any problems please file an issue.