一、常见模块化规范
1. CommonJS(CJS)
- 主要用于 Node.js
- 同步加载模块
- 通过
require和module.exports/exports实现
// a.js
module.exports = {
name: 'test'
}
// b.js
const a = require('./a')
console.log(a.name)
2. ES Module(ESM)
- ES6 官方标准(浏览器 & Node 都支持)
- 使用
import/export - 支持静态分析(这是核心优势)
// a.js
export const name = 'test'
// b.js
import { name } from './a.js'
3. AMD(Asynchronous Module Definition)
- 浏览器端早期方案(代表:RequireJS)
- 异步加载
define(['a'], function(a) {
return a
})
4. CMD(Common Module Definition)
- 阿里提出(代表:SeaJS)
- 按需加载(依赖就近)
define(function(require, exports) {
const a = require('./a')
})
二、CommonJS vs ES Module 核心区别
这是面试高频重点,我帮你总结成“7大差异”👇
1️⃣ 加载机制(最重要)
| CommonJS | ES Module | |
|---|---|---|
| 加载方式 | 运行时加载 | 编译时加载(静态分析) |
| 本质 | 动态 require | 静态 import |
👉 解释:
- CommonJS:代码执行时才加载
- ESM:编译阶段就确定依赖关系(Tree Shaking 依赖这个)
2️⃣ 是否支持 Tree Shaking
- CommonJS ❌ 不支持
- ES Module ✅ 支持
👉 因为 ESM 是静态结构
3️⃣ 导出值的机制(重点)
| CommonJS | ES Module | |
|---|---|---|
| 导出 | 值的拷贝 | 引用绑定(live binding) |
示例:
CommonJS:
let count = 0
module.exports = { count }
count++
👉 引入后是旧值
ES Module:
export let count = 0
count++
👉 引入后是最新值(实时绑定)
4️⃣ 是否可以动态加载
| CommonJS | ES Module | |
|---|---|---|
| 动态加载 | ✅ require | ✅ import() |
👉 但:
require是同步import()是异步 Promise
5️⃣ this 指向
| CommonJS | ES Module | |
|---|---|---|
| 顶层 this | 指向 module.exports | undefined |
6️⃣ 循环依赖处理
| CommonJS | ES Module | |
|---|---|---|
| 行为 | 返回已执行部分 | 更安全(引用绑定) |
👉 ESM 更不容易出问题
7️⃣ 语法差异
| CommonJS | ES Module | |
|---|---|---|
| 导入 | require() | import |
| 导出 | module.exports | export |
三、总结一句话(面试用)
👉 可以直接背这个:
CommonJS 是运行时同步加载,导出的是值的拷贝;
ES Module 是编译时静态加载,导出的是引用绑定,支持 Tree Shaking 和更好的优化。
四、结合你前端经验(加分理解)
你现在用的:
- React / Vue / Vite / Webpack
其实底层:
-
打包工具(如 Vite、Webpack)都会优先解析 ES Module
-
因为:
- 可以 Tree Shaking
- 可以做代码分割(Code Splitting)
五、再补一个容易被问的点
Node.js 中如何区分 CJS 和 ESM?
{
"type": "module"
}
"module"→ 使用 ES Module"commonjs"(默认)→ 使用 CommonJS