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.
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);
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
.
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.
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.
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
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.