CommonJS 是 Node.js 默认模块规范,以文件为单位,通过 module.exports 导出、require() 同步导入;exports 是 module.exports 的引用,仅可添加属性,不可重新赋值;模块缓存且单例,支持动态路径但需避免循环依赖。

CommonJS 是 Node.js 早期默认采用的模块系统,导出和导入都基于运行时同步执行,语义简单直接,适合服务端环境。
导出接口:用 module.exports 或 exports
每个 JS 文件就是一个独立模块,内部变量默认私有。要对外暴露功能,必须显式赋值给 module.exports。
-
module.exports是真正决定模块导出内容的对象,可赋值为任意类型:函数、对象、类、字符串、数字等 -
exports是module.exports的初始引用(即exports = module.exports),只能用于添加属性,不能重新赋值 - 错误写法:
exports = { foo: 'bar' }—— 这会让exports指向新对象,但module.exports仍是空对象,最终导入得到的是空对象 - 正确写法示例:
- 导出多个方法:
exports.add = (a, b) => a + b; exports.PI = 3.14; - 导出单个函数:
module.exports = function greet() { return 'Hi'; }; - 导出类:
module.exports = class Logger { ... }; - 导出实例:
module.exports = new Logger('main');
- 导出多个方法:
导入接口:用 require()
require() 是同步阻塞调用,返回的是模块导出值的浅拷贝(不是实时绑定)。
- 导入整个模块:
const math = require('./math');→ 得到module.exports的值 - 解构使用(本质仍是取对象属性):
const { add, PI } = require('./math'); - 支持动态路径和条件加载:
if (env === 'dev') require('./debug');或require(pathVar); - 注意循环依赖:A require B,B 又 require A 时,A 中未执行完的部分会返回一个空对象或部分初始化的对象
常见误区与关键细节
-
require()返回的是值的拷贝,不是引用绑定 —— 即使导出对象后续被修改,已导入的变量不会自动更新 -
exports不是全局变量,它只在模块顶层作用域有效,且仅作为module.exports的快捷方式 - 模块加载是单例缓存机制:同一路径的
require()多次调用,只执行一次初始化,后续返回缓存结果 - Node.js 中默认识别
.js文件为 CommonJS;若想用 ESM,需改后缀为.mjs或在package.json中设"type": "module"
实际小例子
// logger.jsconst messages = [];exports.log = function(msg) { messages.push(msg); };exports.getLogs = () => [...messages];exports.count = messages.length;
// app.jsconst logger = require('./logger');logger.log('start');console.log(logger.count); // 0 —— 因为 count 是定义时的快照值,不是响应式


















