ES6和CommonJS模块区别

发布于:2025-09-04 ⋅ 阅读:(22) ⋅ 点赞:(0)

ES6 模块(ESM)和 CommonJS 是 JavaScript 中两种主要的模块规范,它们在设计理念、语法和行为上有显著区别。以下是两者的核心差异:

1. 语法规范与用途

  • ES6 模块(ESM)
    是 ECMAScript 2015 标准化的模块系统,适用于浏览器和 Node.js(需显式启用),是 JavaScript 官方模块规范。
    核心关键字:exportimportexport default

  • CommonJS
    是 Node.js 原生的模块规范,主要为服务器端设计,早期用于解决 Node.js 中的模块依赖问题。
    核心关键字:module.exportsexportsrequire()

2. 导出 / 导入语法

操作

ES6 模块(ESM)

CommonJS

默认导出

export default { ... }

module.exports = { ... }

命名导出

export const a = 1; export function f() {}

exports.a = 1; exports.f = function() {}

导入默认

import obj from './module.js'

const obj = require('./module.js')

导入命名

import { a, f } from './module.js'

const { a, f } = require('./module.js')

3. 加载时机与方式

  • ES6 模块:静态加载

    • 导入和导出语句在代码解析阶段执行(编译时加载),而非运行时。

    • 导入路径必须是静态字符串(不能动态拼接)。

    • 支持 import() 动态导入(返回 Promise)。

    示例:

    javascript

    运行

    --javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
    // 静态导入(解析时执行)
    import { utils } from './tools.js';
    
    // 动态导入(运行时执行)
    import('./tools.js').then(module => { ... });
    
  • CommonJS:动态加载

    • 导入(require())在代码运行阶段执行,支持动态路径。

    • 模块加载是同步的(Node.js 中对本地模块有缓存优化)。

    示例:

    javascript

    运行

    --javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
    // 支持动态路径
    const path = './tools.js';
    const utils = require(path);
    

4. 模块输出的本质

  • ES6 模块:值的引用

    • 导出的是值的只读引用,模块内部值变化时,导入方会同步更新。

    • 导入的变量是只读的(不能重新赋值)。

    示例:

    javascript

    运行

    --javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
    // module.js
    export let count = 0;
    export function increment() { count++; }
    
    // main.js
    import { count, increment } from './module.js';
    increment();
    console.log(count); // 输出 1(跟随模块内部变化)
    
  • CommonJS:值的拷贝

    • 导出的是值的拷贝(对象导出的是引用地址的拷贝),模块内部值变化不影响导入方。

    • 导入的变量可以重新赋值。

    示例:

    javascript

    运行

    --javascripttypescriptshellbashsqljsonhtmlcssccppjavarubypythongorustmarkdown
    // module.js
    let count = 0;
    module.exports = {
      count,
      increment: () => { count++; }
    };
    
    // main.js
    const { count, increment } = require('./module.js');
    increment();
    console.log(count); // 输出 0(拷贝的值未更新)
    

5. 循环依赖处理

  • ES6 模块
    由于静态解析,模块在加载时会先创建「模块记录」,循环依赖时能正确引用未完全初始化的模块(返回部分导出值)。

  • CommonJS
    循环依赖时,会返回模块当前已执行部分的导出值(可能是不完整的),依赖后续执行补充。

6. 顶层 this 指向

  • ES6 模块:顶层 this 为 undefined

  • CommonJS:顶层 this 指向当前模块的 module.exports

7. 使用场景

  • ES6 模块

    • 浏览器环境(原生支持,需在 <script type="module"> 中使用)。

    • 现代前端工程(Vue、React 项目,通过 Webpack/Vite 打包)。

    • Node.js 13.2+(需在 package.json 中设置 "type": "module")。

  • CommonJS

    • Node.js 环境(默认模块系统)。

    • 早期 Node.js 生态的库(如 Express 中间件)。

总结

特性

ES6 模块(ESM)

CommonJS

加载时机

解析时(静态)

运行时(动态)

输出本质

值的引用(只读)

值的拷贝(可修改)

循环依赖处理

基于模块记录(更可靠)

基于执行状态(可能不完整)

适用环境

浏览器 + 现代 Node.js

Node.js 原生

动态导入支持

import()(返回 Promise)

require() 天然支持


现代前端开发以 ES6 模块为主,而 CommonJS 主要用于 Node.js 后端开发。在实际项目中,两者可通过打包工具(如 Webpack)相互兼容。