易混淆的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 }
相关推荐
汪子熙24 分钟前
Vite 极速时代的构建范式
前端·javascript
叶常落25 分钟前
[react] js容易混淆的两种导出方式2025-08-22
javascript
爱心发电丶2 小时前
NodeSSh 实现前端自动部署:服务端编译和本地编译
node.js
摸着石头过河的石头2 小时前
大模型时代的前端开发新趋势
前端·javascript·ai编程
洋流3 小时前
0基础进大厂,第22天 : CSS中的定位布局,你的.container还找不到位置吗?
前端·javascript·面试
讨厌吃蛋黄酥3 小时前
面试官问 “React Hooks 为啥不能在条件里用”,这么答才显真水平!多数人只知其然
前端·javascript·面试
Face3 小时前
Node.js全栈基石(壹)
前端·node.js
言兴3 小时前
从输入 URL 到页面显示:深入理解浏览器缓存机制
前端·javascript·面试
讨厌吃蛋黄酥3 小时前
前端跨域难题终结者:从JSONP到CORS,一文搞定所有跨域问题!
前端·javascript·后端
阿星做前端3 小时前
coze源码解读:项目启动
前端·javascript