说说模块化规范?CommonJS和ES Module的区别?

0 阅读2分钟

一、常见模块化规范

1. CommonJS(CJS)

  • 主要用于 Node.js
  • 同步加载模块
  • 通过 requiremodule.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️⃣ 加载机制(最重要)

CommonJSES Module
加载方式运行时加载编译时加载(静态分析)
本质动态 require静态 import

👉 解释:

  • CommonJS:代码执行时才加载
  • ESM:编译阶段就确定依赖关系(Tree Shaking 依赖这个)

2️⃣ 是否支持 Tree Shaking

  • CommonJS ❌ 不支持
  • ES Module ✅ 支持

👉 因为 ESM 是静态结构


3️⃣ 导出值的机制(重点)

CommonJSES Module
导出值的拷贝引用绑定(live binding)

示例:

CommonJS:

let count = 0
module.exports = { count }
count++

👉 引入后是旧值


ES Module:

export let count = 0
count++

👉 引入后是最新值(实时绑定)


4️⃣ 是否可以动态加载

CommonJSES Module
动态加载✅ require✅ import()

👉 但:

  • require 是同步
  • import() 是异步 Promise

5️⃣ this 指向

CommonJSES Module
顶层 this指向 module.exportsundefined

6️⃣ 循环依赖处理

CommonJSES Module
行为返回已执行部分更安全(引用绑定)

👉 ESM 更不容易出问题


7️⃣ 语法差异

CommonJSES Module
导入require()import
导出module.exportsexport

三、总结一句话(面试用)

👉 可以直接背这个:

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