易混淆的CommonJS和ESM(ES Module)及它们区别

前言:
【CommonJs】exports,modules.exports,require的区别

📌概念

1. CommonJS 概念

历史:早期 JavaScript 主要跑在浏览器,没有模块系统;Node.js 为了管理代码,引入了 CommonJS 规范。

核心特点:

  • require() 导入模块
  • module.exportsexports 导出模块
  • 模块在运行时加载(同步加载)
  • 每个文件就是一个独立作用域

👉 示例:

javascript 复制代码
// foo.js
module.exports = { a: 1 };

// bar.js
const foo = require('./foo');
console.log(foo.a); // 1

2. ESM (ES Module) 概念

历史:ES6(2015)标准化了 JavaScript 原生模块系统,叫 ESM。

核心特点:

  • import 导入
  • export 导出
  • 模块在 编译时 静态解析(比 CommonJS 更高效)
  • 支持 tree-shaking(去掉没用到的代码)
  • 既能在浏览器直接运行,也能在 Node.js(新版本)中使用

👉 示例:

javascript 复制代码
// foo.js
export const a = 1;

// bar.js
import { a } from './foo.js';
console.log(a); // 1

📌区别

CommonJS vs ESM 的区别

对比维度 CommonJS ESM
语法 require / module.exports import/ export
加载时机 运行时加载(同步) 编译时加载(静态)
导出方式 整个对象(module.exports) 默认导出 + 具名导出
导入结果 require() 拿到的是对象副本 import 拿到的是 绑定的引用
Tree Shaking ❌ 不支持 ✅ 支持(Webpack、Rollup 等)
是否动态 ✅ 可以写 require(someVar) 动态导入 ❌ 只能静态导入(不过有 import() 动态语法)
执行顺序 按代码执行顺序加载 静态依赖分析后再执行
Node 默认支持 ✅ (默认) ✅ (需 .mjs 后缀 或 package.json "type": "module")
浏览器支持 ❌ 原生不支持(需打包工具) ✅ 现代浏览器原生支持 <script type="module">

📌使用场景

场景

CommonJS:

  • 主要在 Node.js 老项目中使用(兼容性好)
  • 适合脚本、工具类项目

ESM:

  • 现代前端开发的主流选择(React, Vue, Angular 全部用 ESM)
  • Tree-shaking、静态分析、跨平台更优
  • Node.js 16+ 也推荐逐步迁移到 ESM

直观理解

CommonJS 像是:"我执行到这里,才去加载另一个文件"。

ESM 像是:"我在编译时就知道要依赖哪些文件,先解析好,再运行"。

1. CommonJS (Node 里默认用的)

写法:require

导出方式:

javascript 复制代码
// foo.js
// 方法1:exports
exports.a = 1;
exports.sayHi = () => console.log('hi');

// 方法2:module.exports
module.exports = {
  b: 2,
  sayBye: () => console.log('bye')
};

导入方式:

javascript 复制代码
const foo = require('./foo.js');
console.log(foo.a); // 1
foo.sayHi();        // hi
console.log(foo.b); // 2
foo.sayBye();       // bye

👉 CommonJS 里只有 require,没有 import {} 的语法。

2. ES Module (ESM,现代写法)

写法:import

导出方式:

javascript 复制代码
// foo.mjs 或者 package.json 里加 "type": "module"
// 默认导出(只能有一个)
export default function() {
  console.log('default fn');
}

// 具名导出(可以有多个)
export const a = 1;
export function sayHi() { console.log('hi'); }

导入方式:

javascript 复制代码
// 默认导入
import foo from './foo.js'; 
foo(); // default fn

// 具名导入
import { a, sayHi } from './foo.js';
console.log(a); // 1
sayHi();        // hi

3. 关键区别:import xxx vs import { xxx }

写法 对应导出 特点
import xxx from './foo.js' export default ... 只能对应 一个默认导出
import { xxx } from './foo.js' export const xxx = ... / export function xxx... 对应 具名导出,可以同时导入多个
import * as all from './foo.js' 所有导出(默认 + 具名) 用对象的方式访问

4. CommonJS 和 ESM 混用情况

Node 现在也支持 import,但规则有点复杂:

如果模块是用module.exports = { ... }导出的:

javascript 复制代码
// foo.js (CommonJS)
module.exports = { a: 1, b: 2 };

// ESM 导入
import foo from './foo.js';    // 默认导入
console.log(foo.a); // 1

import { a } from './foo.js';  // ❌ 会报错,找不到具名导出

如果模块是用exports.a = ... 导出的:

结果和上面一样,import {a} 在 ESM 里也不生效。

👉 所以 CommonJS 导出的对象 = ESM 的默认导出。

记忆口诀:

  • CommonJSmodule.exports 导出啥,require() 就拿到啥。
  • ESMexport default 对应 import xxxexport const xxx 对应 import { xxx }
相关推荐
Q_Q5110082854 分钟前
python+django/flask的结合人脸识别和实名认证的校园论坛系统
spring boot·python·django·flask·node.js·php
Q_Q5110082855 分钟前
python+django/flask的选课系统与课程评价整合系统
spring boot·python·django·flask·node.js·php
xump14 分钟前
如何在DevTools选中调试一个实时交互才能显示的元素样式
前端·javascript·css
Front_Yue17 分钟前
深入探究跨域请求及其解决方案
前端·javascript
风止何安啊20 分钟前
JS 里的 “变量租房记”:闭包是咋把变量 “扣” 下来的?
前端·javascript·node.js
有点笨的蛋27 分钟前
深入理解 JavaScript 原型机制:构造函数、原型对象与原型链
前端·javascript
晴栀ay30 分钟前
JS中原型式面向对象的精髓
前端·javascript
3秒一个大30 分钟前
JavaScript 原型详解:从概念到实践
javascript
undsky38 分钟前
【RuoYi-Eggjs】:把 MyBatis 带到 Node.js
node.js
Amy_yang43 分钟前
js 封装时间格式化,将单位有秒(s)的数据转换为'00:00:00'格式
javascript