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.