易混淆的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 }
相关推荐
冰暮流星15 分钟前
javascript逻辑运算符
开发语言·javascript·ecmascript
拖拉斯旋风2 小时前
从零开始:使用 Ollama 在本地部署开源大模型并集成到 React 应用
前端·javascript·ollama
德育处主任2 小时前
『NAS』在群晖部署图片压缩工具-Squoosh
前端·javascript·docker
Van_captain3 小时前
rn_for_openharmony常用组件_Breadcrumb面包屑
javascript·开源·harmonyos
静听松涛1333 小时前
提示词注入攻击的防御机制
前端·javascript·easyui
澄江静如练_3 小时前
优惠券提示文案表单项(原生div写的)
前端·javascript·vue.js
C_心欲无痕3 小时前
ts - 关于Object、object 和 {} 的解析与区别
开发语言·前端·javascript·typescript
全栈前端老曹4 小时前
【包管理】read-pkg-up 快速上手教程 - 读取最近的 package.json 文件
前端·javascript·npm·node.js·json·nrm·package.json
水冗水孚4 小时前
告别黑盒!手写Windows版简易NodeMON,学习文件监听代码修改与进程服务重启知识
node.js·express
程序员爱钓鱼4 小时前
Node.js 编程实战:测试与调试 —— 调试技巧与性能分析
前端·后端·node.js