RxJS — Reduce Angular app bundle size using lettable operators

Written by Sureshkumar_Ash | Published 2017/12/22
Tech Story Tags: javascript | angular | angular-cli | rxjs | reactive-programming

TLDRvia the TL;DR App

RxJS version 5. 5 introduced an impactful change in the import process of RxJS and added lettable operators. This blog post is a small comparison of how the lettable operators improve the bundle size of your application. I am choosing Angular as my framework of choice to showcase this, but this can be done in vanilla js or any framework.

Approach

  • Changes in RxJS import process
  • Introduction to lettable operators
  • Implement a small application using RxJS < 5.5
  • Collect metrics on bundle sizes on RxJS < 5.5
  • Upgrade to RxJS 5.5 and update the application
  • Collect metrics on upgraded application

Changes in the imports when using RxJS

In version 5.5, we can import operators/creation utils using es6 imports.

import { map, scan, filter } from 'rxjs/operators';

Any operator (function) can be imported from rxjs/operators

Observable creation methods have also been updated.

import { of } from 'rxjs/observable/of';import { from } from 'rxjs/observable/from';import { range } from 'rxjs/observable/range';

const source$ = of(1,2,3);

const rangeSource$ = range(0,5);

Introduction to lettable operators

Lettable Operators are functions that accept an observable and return an observable. A new method has been introduced to Observable’s prototype called pipe. Using pipe we can compose n-number of functions to act on our observable-emitted values. Lets see an example of summing the squares of odd numbers in a given range using map, scan and filter.

Operator which are part of the prototype of the observable are not tree-shakable by webpack or other bundlers. This will increase the bundle size since the operators becomes part of the bundle even if they are not used. Lettable operators, however, are pure functions. When unused, they are excluded from the bundle. Even linters can identify that the functions are declared but not used anywhere.

Tree-shaking is the process of dead code elimination.

Note: A few operators have been renamed to avoid conflicts with Javascript keywords

do -> tapswitch -> switchAllcatch -> catchErrorfinally -> finalize

At this point, lets say we want to perform a side effect of console logging the values after every transformation. We can perform that by adding the renamed method, tap.

Angular Application in RxJS < 5.5

I am going to use an Angular application which I have used in few of my previous blog posts.

demo: https://ashwin-sureshkumar.github.io/angular-cache-service-blog/

github: https://github.com/ashwin-sureshkumar/angular-cache-service-blog

I am not going to discuss how this application was built since it has been covered in a previous blog post. This post focuses on RxJS and an application’s build metrics.

Collect metrics on bundle sizes RxJS < 5.5

To visualize our build metrics, we need to install webpack-bundle-analyzer

npm install webpack-bundle-analyzer --save-dev

Now, time to build our application.

ng build --prod --stats-json

// Above command builds our application in prod mode and also, // generates stats.json, and stores it in /dist/stats.json

To visualize our metrics, run the below command.

webpack-bundle-analyzer dist/stats.json

This will open up an app in the browser.

In the above screenshot, focus on the rxjs section. We can see that every single operator, util, scheduler and observable type is imported into our vendor even though we do not utilize a majority of them in our application. This is unnecessary code being shipped with our application. Focus on the rxjs stats below.

Upgrade our application to RxJS 5.5

Lets upgrade our sample application to RxJS 5.5, Angular version to 5.1, and install the required peer dependencies.

$ npm install @angular/{animations,common,compiler,compiler-cli,core,forms,http,platform-browser,platform-browser-dynamic,router}@5.1.1

Thanks to Igor Minar, we know that tree-shaking in Angular apps is dependent on the Angular build-optimizer package, so make sure you install it.

// command to run

npm install @angular-devkit/build-optimizer

The major changes to our app are in the infinite-scroll-directive, hackernews service and AppComponent

Collect metrics on the upgraded application

We have installed the bundle analyzer in the previous step, so lets build the app and visualize our metrics.

ng build --prod --stats-json

// once build is finished

webpack-bundle-analyzer dist/stats.json

In the above metrics visual, you can see the rxjs section doesn’t contain the entire list of operators, observable, util functions, etc., The package is pretty small compared to what we saw previously. Below is a closer look at the stats.

Please be aware to make sure that the external libraries you are using are following the same approach. If not, their imports will affect your bundle size.

Though this post describes how to use lettable operators to reduce your bundle sizes, I hope it kindles your curiosity in bundle sizes, performance and how to build applications with performance in mind. If you liked this post, please share, comment and recommend.


Published by HackerNoon on 2017/12/22