易混淆的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 }
相关推荐
BillKu3 小时前
Vue3 + Element-Plus 抽屉关闭按钮居中
前端·javascript·vue.js
DevilSeagull3 小时前
JavaScript WebAPI 指南
java·开发语言·javascript·html·ecmascript·html5
大怪v4 小时前
前端佬:机器学习?我也会啊!😎😎😎手“摸”手教你做个”自动驾驶“~
前端·javascript·机器学习
gnip6 小时前
链式调用和延迟执行
前端·javascript
杨天天.6 小时前
小程序原生实现音频播放器,下一首上一首切换,拖动进度条等功能
前端·javascript·小程序·音视频
Dragon Wu7 小时前
React state在setInterval里未获取最新值的问题
前端·javascript·react.js·前端框架
YU大宗师7 小时前
React面试题
前端·javascript·react.js
给月亮点灯|8 小时前
Vue基础知识-Vue集成 Element UI全量引入与按需引入
前端·javascript·vue.js
三思而后行,慎承诺9 小时前
Reactnative实现远程热更新的原理是什么
javascript·react native·react.js
知识分享小能手9 小时前
React学习教程,从入门到精通,React 组件生命周期详解(适用于 React 16.3+,推荐函数组件 + Hooks)(17)
前端·javascript·vue.js·学习·react.js·前端框架·vue3