CommonJS 是 JavaScript 的模块化规范,主要应用于 服务器端环境(尤其是 Node.js),其核心目标是解决代码组织、依赖管理和作用域隔离问题 。以下是其核心要点:
🔧 一、核心特性
同步加载
模块通过require()
同步加载并执行,后续代码需等待模块加载完成后执行,适用于 I/O 快速的服务器环境(如本地文件读取) 。作用域隔离
每个文件视为独立模块,模块内定义的变量、函数默认私有(不污染全局作用域),仅通过导出接口对外暴露功能 。// math.js const privateVar = 10; // 私有变量 module.exports = { add: (a, b) => a + b + privateVar }; // 导出接口
导出与导入语法
- 导出:使用
module.exports
或exports
对象(注:exports
本质是module.exports
的引用) 。// 正确导出(推荐直接赋值 module.exports) module.exports = { add, subtract }; // 或追加属性(避免 exports 被重写) exports.add = (a, b) => a + b;
- 导入:通过
require(模块路径)
同步加载其他模块 。const math = require('./math'); console.log(math.add(1, 2)); // 输出 13(含私有变量值)
- 导出:使用
缓存机制
模块首次加载后被缓存,后续调用require()
直接返回缓存结果,避免重复执行 。可通过delete require.cache[modulePath]
清除缓存强制重新加载。
⚙️ 二、实现原理
模块包装
Node.js 将模块代码包裹在立即执行函数中,注入module
、exports
、require
等变量:(function(exports, require, module, __filename, __dirname) { // 模块代码 });
从而实现作用域隔离和局部变量私有化 。
路径解析与加载
require()
根据路径规则(./
、../
、绝对路径或核心模块名)定位文件,按.js
→.json
→.node
顺序解析,支持目录加载(优先找package.json
的main
字段或index.js
) 。循环依赖处理
当模块 A 依赖 B,B 又依赖 A 时:- B 加载时会获取 A 的未完成导出对象(此时 A 可能仅执行了部分代码) 。
- 依赖执行顺序影响导出结果(需合理设计代码结构避免逻辑混乱)。
⚠️ 三、局限性与适用场景
场景 | 优势 | 局限性 |
---|---|---|
服务器端 | 同步加载无阻塞,代码简洁易维护 | 不适用于浏览器(网络请求阻塞) |
模块复用 | 清晰的依赖管理,避免全局污染 | 动态导入导致静态分析困难 |
生态兼容 | Node.js 原生支持,生态成熟 | 浏览器端需编译工具转换(如 Webpack) |
🔄 四、演进与现状
- 历史地位:CommonJS 填补了服务端 JavaScript 模块化的空白,成为 Node.js 的基石 。
- 现代替代:ES Modules(ESM)凭借静态分析、异步加载等优势成为语言标准,逐步取代 CommonJS 。
- 过渡方案:Node.js 支持
.mjs
扩展名或package.json
的"type": "module"
启用 ESM,同时兼容 CommonJS 语法 。
💡 总结:CommonJS 是服务器端模块化的经典方案,其同步加载、闭包隔离和缓存机制高效支撑了 Node.js 生态,但浏览器兼容性不足推动 ESM 成为未来主流 。