ES Module(ESM)和 CommonJS(CJS)是 JavaScript 中两种主流的模块化规范。ESM 是 ES6 推出的官方标准,而 CommonJS 则是 Node.js 早期采用的模块化方案。
以下从几个核心角度为你详细拆解:
1. 核心差异速览表
| 对比角度 | CommonJS (CJS) | ES Module (ESM) |
|---|---|---|
| 基本语法 | require() 导入,module.exports 导出 |
import 导入,export 导出 |
| 加载时机 | 运行时加载(动态) | 编译时加载(静态) |
| 加载方式 | 同步加载 | 异步加载(浏览器端) |
| 导出本质 | 值的拷贝(浅拷贝) | 值的引用(Live Binding) |
| 代码优化 | 不支持 Tree Shaking | 支持 Tree Shaking |
顶层 this |
指向 module.exports |
undefined(严格模式) |
2. 深度解析各个角度
语法与规范来源
- CommonJS :是社区提出的规范,主要用于 Node.js 服务端环境。它的语法非常直观,使用
require()来引入模块,使用module.exports或exports来向外暴露功能。 - ES Module :是 ECMAScript 2015 (ES6) 的官方语言标准,旨在统一浏览器和服务端的模块化。它使用
import和export关键字,语法更加语义化,支持命名导出和默认导出。
加载时机与方式(最核心的区别)
- CommonJS 是"运行时同步加载" :当你代码执行到
require()这一行时,才会去加载并执行对应的模块文件。这种方式在服务端(读取本地硬盘文件)非常高效,但在浏览器端会因为网络请求阻塞页面渲染,所以浏览器不原生支持。 - ES Module 是"编译时静态加载" :JS 引擎在解析代码的阶段(编译时),就会通过分析
import和export语句,提前确定好模块之间的依赖关系。在浏览器中,ESM 默认是异步加载的,不会阻塞 HTML 的解析。
导出的本质:值拷贝 vs 值的引用 这是两者在实际开发中最容易产生 Bug 的差异点:
-
CommonJS(值拷贝) :导出的是模块内部变量的一个副本 。如果模块内部修改了这个变量,外部引入的地方是感知不到 的。
javascript// CommonJS 示例 // counter.js let count = 0; module.exports = { count }; setTimeout(() => { count = 1; }, 1000); // 内部修改 // main.js const { count } = require('./counter.js'); console.log(count); // 0 setTimeout(() => { console.log(count); }, 1100); // 依然是 0,因为是拷贝的旧值 -
ES Module(值的引用 / Live Binding) :导出的是对模块内部变量的动态引用 。当模块内部修改了变量,所有引入该变量的地方都会同步更新。
javascript// ESM 示例 // counter.js export let count = 0; setTimeout(() => { count = 1; }, 1000); // 内部修改 // main.js import { count } from './counter.js'; console.log(count); // 0 setTimeout(() => { console.log(count); }, 1100); // 1,实时同步了最新值
代码优化(Tree Shaking)
- ES Module :由于它是静态的,打包工具(如 Webpack、Rollup、Vite)可以在打包阶段就分析出哪些代码被使用了,哪些没有。未被使用的代码(Dead Code)会被直接剔除,这个过程叫 Tree Shaking(摇树优化),能显著减小打包体积。
- CommonJS :由于
require()可以在代码运行时动态执行(比如写在if判断里),打包工具很难在编译阶段确定到底引用了哪些模块,因此无法有效支持 Tree Shaking。
运行环境与兼容性
- CommonJS:Node.js 的默认模块规范,生态极其成熟。在浏览器中无法直接使用,必须通过 Webpack、Browserify 等工具打包转换。
- ES Module :现代浏览器原生支持(通过
<script type="module">),也是现代前端框架(Vue3, React)和构建工具(Vite)的首选。Node.js 从 v12 版本后也开始支持 ESM,但需要在package.json中配置"type": "module"或使用.mjs后缀。
总结建议: 在现代前端开发和新的 Node.js 项目中,优先推荐使用 ES Module,因为它更标准、性能更好且支持代码优化。但在维护一些老旧的 Node.js 项目或依赖某些仅支持 CJS 的第三方库时,你依然会频繁接触到 CommonJS。