- 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 asserverJs
.- To import the module we use the
require
keyword and to export functions, we add all the functions into theexports
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 thenls -l
in the console. the output should be something like this.if you look at the size of the
bundle.js
it is 90114 bytes(90.114 KB ๐คฏ๐คฏ) . in thebundle.js
file it haslodash.js
and all the code fromhelper.js
.- but in
index.js
, we are only using thefilter
function. so ideally it will be awesome if we only have thefilter
function in ourbundle.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 theexport
keyword before function likeexport 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 thenls -l
into the console. the output should be something like this.size of the
bundle.js
is only 139 bytes ๐๐. which is 215 times lesser thanbundle.js
created from CommonJs. Why is that?? The short answer is whenever we are creating a bundle file withwebpack
orrollup
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 havelodash
,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 bywebpack
androllup
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 exactlyindex.js
is using it in the code. so it will add all the code in the finalbundler.js
file.But, bundlers like
rollup
andwebpack
support basic treeshaking forCommonJS
. 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.