CommonJS vs. ES Modules: The Ultimate Guide to JavaScript Modularity

Alexander Parks
6 min readFeb 19, 2024

--

Why are there CommonJs and Es Module?

We all know that in the early days, the concept of JavaScript module was introduced into the js file code through the script tag. Of course, there is nothing wrong with writing these basic simple requirements, but when our project becomes larger and larger, the more js files we introduce, the following problems will arise:

  • js file scopes are all top-level, which will cause variable pollution.
  • There are many js files and it becomes difficult to maintain.
  • js file dependency problem, if you don’t pay attention to the order, errors will be introduced, and all codes will report errors.

In order to solve the above problems, JavaScript community appeared CommonJs , CommonJs is a modular specification, including the current NodeJs Part of the CommonJs syntax is in it. Then later the Es6 version officially added the Es Module module. Both of these solve the above problems, so what problems do they solve?

  • Solve the problem of variable pollution. Each file has an independent scope, so there is no variable pollution.
  • Solve code maintenance problems, the code in one file is very clear.
  • Solve the file dependency problem. You can clearly see which other files a file depends on.

So let’s learn about their syntax and disadvantages one by one.

CommonJs basic syntax

Export

Use module.exports in CommonJs to export variables and functions. You can also export any type of value. See the following case.

module.exports = {
name: "Tom",
age: 24,
sex: "male"
}

module.exports.name = "Tom"
module.exports.sex = null
module.exports.age = undefined

Export directly

You can also omit the module keyword for export, or write exports directly to export. See the following case.

exports.name = "Tom"
exports.sex = "male"

Note: If you use exports to export a single value, you cannot export an object value. This will only modify the object of exports. However, the modification is invalid. The final export is still name and sex, because the final export is determined by module.exports. .

exports.name = "Tom"
exports.sex = "male"
exports = {
name: "Tom"
}

In the above example, this situation will change the reference value of the object and the export will be invalid, so the final export will still be name and sex .

Mixed export

Mixed export, exports and module.exports can be used at the same time without problems.

exports.name = "Tom"
module.exports.age = 24

Import

CommonJs can be imported using the require syntax. If you want a single value, you can get it by destructuring the object.

// index.js
module.exports.name = "Tom"
module.exports.age = 24
let data = require("./index.js")
console.log(data) // { name: "Tom", age: 24 }

Repeat import

Whether it is CommonJs or Es Module , they will not be imported repeatedly. That is, as long as this file has been loaded once, it will not take effect if I import it again.

let data = require("./index.js")
let data = require("./index.js")// no more repeat executions

Dynamic import

CommonJs supports dynamic import. What does it mean? You can use the require syntax in a statement. Let’s take a look at the following case.

let lists = ["./index.js", "./config.js"]
lists.forEach((url) => require(url)) // dynamic import
if (lists.length) {
require(lists[0]) // dynamic import
}

Changes in imported values

CommonJs The imported value is copied, so the copied value can be modified, but this will cause variable contamination, and the name may be duplicated accidentally.

// index.js
let num = 0;
module.exports = {
num,
add() {
++ num
}
}
let { num, add } = require("./index.js")
console.log(num) // 0
add()
console.log(num) // 0
num = 10

In the above example, you can see that the exported value of exports is a copy of the value. After changing the value of ++ num , the value has not changed, and we have also imported the value of num . Can be modified

Summary

CommonJs solves the problems of variable pollution, file dependency, etc. We also introduced its basic syntax above. It can be imported dynamically (the code occurs at runtime) and cannot be imported repeatedly.

Es Module basic syntax

Export

There are two types of exports in Es Module , single export ( export ) and default export ( export default ). Single export is not like CommonJs can import the values ​​I want. Then the default export is to import everything directly. Of course, any type of value can also be exported in Es Module .

export const name = "Tom"
export const age = 24

export function fn() {}
export const test = () => {}

const name = "Tom"
const sex = "male"
export { name, sex }

Mixed export

You can use export and export default at the same time without affecting each other. You only need to pay attention when importing. If there is a mixed import in the file, you must import the default export first, and then import a single Imported value.

export const name = "Tom"
export const age = 24
export default {
fn() {},
msg: "hello Tom"
}

Import

Es Module uses the import syntax for import. If you want a single import, you must use curly braces {} . Note: the curly braces here are different from destructuring.

// index,js
export const name = "Tom"
export const age = 24
import { name, age } from './index.js'
console.log(name, age) // "Tom" 24

import * as all from './index.js'
console.log(all) // {name: "Tom", age: 24}

Mixed import

Mixed import, then mixed import is used in this file. The import statement must be the default export first, and then the single export. The order must be correct or an error will be reported.

// index,js
export const name = "Tom"
export const age = 24
export default {
msg: "Tom"
}
import msg, { name, age } from './index.js'
console.log(msg) // { msg: "Tom" }

In the above example, if the imported name does not want to be the same as the original local name, you can create an alias.

// index,js
export const name = "Tom"
export const age = 24
export default {
msg: "Tom"
}
import { default as all, name, age } from './index.js'
console.log(all) // { msg: "Tom" }

Changes in imported values

export The exported value is a reference to the value, and there is an internal mapping relationship. This is the role of the export keyword. Moreover, the imported value cannot be modified, that is, it is in a read-only state.

// index.js
export let num = 0;
export function add() {
++ num
}
import { num, add } from "./index.js"
console.log(num) // 0
add()
console.log(num) // 1
num = 10 // throw error

Es Module is static

Es Module import statement can only be declared at the top of the file, can not dynamically load statements, Es Module statement runs during code compilation.

if (true) {
import xxx from 'XXX' // error
}

Summary

Es Module also solves the problem of variable pollution and dependency order. The syntax of Es Module is also more flexible. The exported values ​​​​are also exported references. The exported variables are in a readable state, which strengthens the code readability.

Difference Between CommonJs and Es Modules

CommonJs

  • CommonJs can load statements dynamically, and the code occurs at runtime.
  • CommonJs mixed export is still a syntax, but there is no need to declare the previous object. When I export the reference object, the previous export is overwritten.
  • CommonJs exported values ​​are copies, and the exported values ​​can be modified. This makes it difficult to troubleshoot and cause variable pollution when code errors occur.

Es Module

  • Es Module is static and cannot dynamically load statements. It can only be declared at the top of the file. The code occurs at compile time.
  • Es Module mixed export, single export, default export, completely independent of each other.
  • When Es Module exports, there is a mapping relationship before the reference value, and the values ​​​​are readable and cannot be modified.

--

--

No responses yet