← [ BACK_TO_NOTES ]

CommonJS

解释 Node 传统 CommonJS 模块的导出/导入、同步加载与和 ESM 的互操作边界。

#concept #growing #dev #javascript #nodejs

[!info] related notes

CommonJS

这个概念为什么出现

Node 早期用 require/module.exports 解决“文件如何变成可复用模块”。今天 ESM 是语言标准,但大量包与旧代码仍是 CJS,工程上必须会读会互操作。

[!abstract] 一句话理解 CommonJS 以同步 require 加载模块,并通过 exports/module.exports 暴露值;它是 Node 历史默认,不是浏览器原生模块系统。

最小可运行示例

场景:把库存计价函数拆成工具模块

// pricing.cjs
function priceWithTax(base, rate) {
  return base * (1 + rate)
}
module.exports = { priceWithTax }
// main.cjs
const { priceWithTax } = require('./pricing.cjs')
console.log(priceWithTax(100, 0.03))
node main.cjs

期望:103。

核心模型

  • 每个文件是模块,有自己的作用域
  • 首次 require 通常同步执行目标 CJS 模块,后续按解析后的文件名命中缓存;不同解析路径可能得到不同实例
  • require 返回目标的 module.exports 值,可以是对象、函数或原始值;不会自动复制对象
  • exports 初始指向 module.exports;重绑任一变量后,两者可能不再指向同一对象
  • 循环依赖时可能拿到不完整导出

值、对象与绑定

// counter.cjs
exports.count = 0
exports.increment = () => { exports.count++ }
// counter-main.cjs
const counter = require('./counter.cjs')
const { count } = counter
counter.increment()
console.log(counter.count, count) // 1 0
console.log(counter === require('./counter.cjs')) // true

解构得到的数字是当时的值,counter 则仍指向同一导出对象。这与 ESM 的导入绑定跟随导出变量变化不同。

与 ESM 边界

点CJSESM
语法require/exportsimport/export
加载同步为主静态分析/异步能力
浏览器不原生原生 type=module
互操作需 Node 解析规则有条件导出等

Node 的 .cjs 明确表示 CJS,.mjs 明确表示 ESM;.js 的解释还受包配置与 Node 版本影响。新项目宜显式声明格式。

在 Node 24 中,require() 可以加载符合条件、整个依赖图不含顶层 await 的同步 ESM;异步 ESM 应使用 import()。ESM 中使用 CJS 风格的解析可通过 node:module 的 createRequire,不应把不同 Node 版本的互操作行为混为一谈。

常见误区

[!warning] 常见误区:exports = {} 重绑定 应 module.exports = {},只给 exports 赋值不会改变导出。

工程实践

  • 新 Node 代码优先 ESM(项目策略允许时)
  • 维护双包时理解 exports 字段条件导出
  • 打包工具会处理 CJS/ESM 转换,但不要盲目

本节总结

CJS 是 Node 历史模块合同;会它是为了生态与迁移,不是为了在新前端项目继续扩散。

自测题

  1. 为什么 require 有缓存?
  2. ESM 浏览器能否直接 require?
参考答案
  1. 避免重复执行,并让解析为同一文件的调用共享导出;不是所有同名包都共享一个实例。
  2. 不能;浏览器原生是 ESM。

参考资料

创建于 2025/1/1 更新于 2026/10/6
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本