- Technicalpig
- Posts
- TechnicalPig🐷: CommonJS vs ECMAScript Modules
TechnicalPig🐷: CommonJS vs ECMAScript Modules
What are they and their advantages and limitations
CommonJS (CJS) and ECMAScript Modules (ESM) are 2 different standards for writing and using modules in Javascript, each with their own set of features and use cases.
The differences between CJS and ESM are primarily rooted in their syntax, module loading mechanism, and runtime behaviour.
1. Syntax
CommonJS: Uses
require()
for importing modules andmodule.exports
orexports
for exporting modules.// Importing with CommonJS const lodash = require('lodash'); // Exporting with CommonJS module.exports = someFunction;
ECMAscript Modules: Uses
import
andexport
keywords for importing and exporting modules.// Importing with ESM import lodash from 'lodash'; // Exporting with ESM export default someFunction; export { someFunction };
2. Module Loading Mechanism
CommonJS: Synchronous loading of modules. This is straightforward and simple for server-side code (like in Node.js) where modules are loaded from the local filesystem.
ECMAscript Modules: Supports both synchronous and asynchronous module loading, allowing for dynamic imports. This is particularly beneficial for browsers where modules can be fetched over the network.
3. Runtime Behavior
CommonJS: Modules are loaded, compiled, and executed at runtime, allowing for certain dynamic coding patterns, like conditional imports.
ECMAscript Modules: Have a static structure defined by the
import
/export
statements. This allows for optimisations like tree shaking (removing unused exports), static analysis, and more predictable code splitting in bundlers.
4. Usage
CommonJS: Historically used in Node.js and for server-side development.
ECMAscript Modules: Initially designed for the browser to enable module usage directly without bundlers, but now also supported in Node.js (from version 12.x onwards with the
.mjs
extension or setting"type": "module"
inpackage.json
for.js
files).
5. Top-level await
CommonJS: Does not support top-level
await
in modules.ECMAscript Modules: Supports top-level
await
, allowing modules to await resources asynchronously before they are used.
CommonJS: Benefits and Drawbacks
Benefits:
Synchronous Loading: Modules are loaded synchronously, which simplifies the module resolution and execution order.
Wide Adoption in Node.js: CommonJS is the original module system used in Node.js, meaning it has wide support and a large ecosystem of modules built with it.
Dynamic Imports: Allows for dynamic calculation of module paths and conditional module loading, providing flexibility in certain scenarios.
Drawbacks:
Not Suitable for the Browser: Synchronous module loading can be inefficient in browser environments.
the use of bundlers or transpilers is necessary for efficient browser support because these tools transform the modules from a synchronous, server-optimised format into an optimised, browser-friendly bundle. They handle the differences in module loading mechanisms, resolve dependencies ahead of time, and improve load performance through optimisations, making it feasible to use CommonJS modules in web applications.
bundlers also perform optimisations such as minification (removing unnecessary characters), tree shaking (eliminating unused code), and module splitting (for loading code on demand).
No Tree Shaking: The static analysis required for optimisations like tree shaking (eliminating unused code) is not possible, potentially leading to larger bundle sizes in web applications.
ECMAScript Modules: Benefits and Drawbacks:
Benefits:
Static Structure: Enables static analysis, allowing for advanced optimisations like tree shaking, which can significantly reduce bundle sizes in web applications.
Dynamic Imports: Supports dynamic
import()
expressions, enabling code splitting and lazy loading of modules, which is beneficial for performance in web applications. This is useful when you want to conditionally load modules.Top-Level
await
: simplifies the handling of asynchronous operations at the module level as modules can asynchronously wait for their resources to be loaded before the module is considered fully loaded and its exports available to other modules.Official ECMAScript Standard: Being part of the ECMAScript standard means that ESM has native support in modern JavaScript engines and browsers, ensuring compatibility and future-proofing.
Better for the Browser: Natively supported by modern web browsers, eliminating the need for module bundlers or transpilar to load modules in browser.
while the server-side code itself is not directly "read" by the browser in the traditional sense, modern web development practices often involve executing the same or similar code across both server and client environments for reasons such as performance optimisation, SEO, and code reuse. This necessitates certain code and logic to be designed in a way that is compatible with both server and browser execution environments.
Drawbacks:
Asynchronous Loading: While beneficial for the web, asynchronous loading can introduce complexity in the module resolution and execution order, which may require additional tooling or patterns to manage effectively.
Node.js Compatibility: Requires specific file extensions (
.mjs
for modules) or package.json configuration in Node.js, which can add overhead to project setup and maintenance.