Why you should avoid CommonJs on your client-side javascript

Why you should avoid CommonJs on your client-side javascript

ยท

4 min read

  • CommonJs and ECMAScript module are the most famous module system in the javascript ecosystem. Before we consider why we should not use the CommonJs module system on the client-side, let's look at what exactly CommonJs and ESM are and how do they work?

    CommonJs:-

  • Commonjs module system was specially introduced to work with the server-side javascript (node.js). that is why initially it was also known as serverJs.
  • To import the module we use the require keyword and to export functions, we add all the functions into the exports object.
// helper.js

const { mapKeys } = require('lodash-es');

function filter(predicate_fn,arr) {
      let newList= [];
      for (let [index, val] of arr.entries()) {
        if (predicate_fn(val, index, arr)) {
          newList.push(val);
        }
      }
      return newList;
}

function map(mapper,arr){
      let mappedArray = [];
      for (let [index, val] of arr.entries()) {
        mappedArray.push(mapper(val, index, arr));
      }
      return mappedArray;
}

module.exports = {
    filter,
    map
}
  • now import these functions into index.js.
// index.js

const {filter} = require('./helper');

const evenNumbers= filter((x)=>{return x % 2 === 0},[1,2,3,4,5])

console.log(evenNumbers);
  • we will bundle this application using webpack with the following config.
const path = require('path');
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: path.resolve(__dirname, 'dist')
  },
  mode: 'production'
};
  • now let's analyze the bundled code. I have created the sample of this application Here. just go there and type cd dist and then ls -l in the console. the output should be something like this. Screenshot from 2021-09-14 14.13.28.png

  • if you look at the size of the bundle.js it is 90114 bytes(90.114 KB ๐Ÿคฏ๐Ÿคฏ) . in the bundle.js file it has lodash.js and all the code from helper.js.

  • but in index.js, we are only using the filter function. so ideally it will be awesome if we only have the filter function in our bundle.js. this is where ESM comes into play.

ECMAScript module :-

  • In this module system you can import the function using import {f} from 'package'; and export the function from the module by just adding the export keyword before function like export function name(){}
  • now let's convert the above example into ESM and compare the bundle size.
// helper.js

import { mapKeys } from 'lodash-es';

export function filter(predicate_fn, arr) {
  let newList = [];
  for (let [index, val] of arr.entries()) {
    if (predicate_fn(val, index, arr)) {
      newList.push(val);
    }
  }
  return newList;
}

export function map(mapper, arr) {
  let mappedArray = [];
  for (let [index, val] of arr.entries()) {
    mappedArray.push(mapper(val, index, arr));
  }
  return mappedArray;
}
// index.js

import { filter } from './helper';

const evenNumbers = filter(
  x => {
    return x % 2 === 0;
  },
  [1, 2, 3, 4, 5]
);

console.log(evenNumbers);
  • You can find a sample of this application Here. Go to the application and type cd dist and then ls -l into the console. the output should be something like this. Screenshot from 2021-09-14 15.34.04.png

  • size of the bundle.js is only 139 bytes ๐ŸŽ‰๐ŸŽ‰. which is 215 times lesser than bundle.js created from CommonJs. Why is that?? The short answer is whenever we are creating a bundle file with webpack or rollup and if we are using ECMAScript module, bundler will only include the code that we are using but in the case of CommonJs, it will bundle all the code. so in CommonJS bundle file we will have lodash, map function. even though we are not using that code.

  • Removing dead code and adding only the code that we are using is also known as treeshaking. (thus tree in cover photo). now let's look at why CommonJs does not support tree shaking.

(Note: CommonJs is perfectly good for server-side javascript because in server-side bundle size is not the issue)

Why no treeshaking in Commonjs ??

  • Well, simple answer that CommonJs supports dynamic export name. which sometimes cannot be determined by webpack and rollup at build time. so it can not determine which code is used and which is not.
module.exports = {
[getCookie(Math.random())] : ()=>{...}
}
  • Here, we don't know exactly the name of the exported function which can only be determined at runtime(in this case running in the browser).so bundler will not know what exactly index.js is using it in the code. so it will add all the code in the final bundler.js file.

  • But, bundlers like rollup and webpack support basic treeshaking for CommonJS. but it has limitations.

conclusion :-

  • small bundle size is one of the easiest ways to increase the performance of a web app. For that, you should avoid the CommonJs module system and npm packages that use CommonJS.
  • To check if the npm package uses ESM, you check out the is-esm package.
  • That's it, folks. if you like my content, please follow me on Hashnode, and don't forget to like the post.