前言:
【CommonJs】exports,modules.exports,require的区别
📌概念
1. CommonJS 概念
历史:早期 JavaScript 主要跑在浏览器,没有模块系统;Node.js 为了管理代码,引入了 CommonJS 规范。
核心特点:
- 用 
require()导入模块 - 用 
module.exports或exports导出模块 - 模块在运行时加载(同步加载)
 - 每个文件就是一个独立作用域
 
👉 示例:
            
            
              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 的默认导出。
记忆口诀:
- CommonJS → 
module.exports导出啥,require()就拿到啥。 - ESM → 
export default对应import xxx;export const xxx对应import { xxx }。