模块化规范:ESM与CJS 的差异

基本概念

ESM(ECMAScript Modules)CJS(CommonJS) 是 JavaScript 中两种不同的模块系统。它们的设计目标、语法和使用场景有所不同。以下是它们的主要差异:


1. 语法差异

(1) ESM

  • 导出模块 :使用 export 关键字。

    javascript 复制代码
    // 导出变量
    export const name = "Alice";
    
    // 导出函数
    export function greet() {
      console.log("Hello!");
    }
    
    // 默认导出
    export default function() {
      console.log("Default export");
    }
  • 导入模块 :使用 import 关键字。

    javascript 复制代码
    // 导入命名导出
    import { name, greet } from './module.js';
    
    // 导入默认导出
    import myFunction from './module.js';

(2) CJS

  • 导出模块 :使用 module.exportsexports

    javascript 复制代码
    // 导出变量
    exports.name = "Alice";
    
    // 导出函数
    module.exports.greet = function() {
      console.log("Hello!");
    };
    
    // 默认导出
    module.exports = function() {
      console.log("Default export");
    };
  • 导入模块 :使用 require 函数。

    javascript 复制代码
    // 导入模块
    const myModule = require('./module');
    
    // 使用导出的内容
    console.log(myModule.name);
    myModule.greet();

2. 加载方式

(1) ESM

  • 静态加载 :ESM 是静态的,模块的依赖关系在代码解析阶段就确定了。

    • import 语句必须位于模块的顶层,不能动态加载。
    • 支持静态分析,便于工具进行优化(如 Tree Shaking)。
  • 异步加载 :ESM 支持异步加载模块(通过 import() 动态导入)。

    javascript 复制代码
    import('./module.js').then(module => {
      module.greet();
    });

(2) CJS

  • 动态加载 :CJS 是动态的,模块的依赖关系在运行时确定。

    • require 可以在代码的任何地方调用,甚至可以动态加载模块。
    • 不支持静态分析,难以进行 Tree Shaking。
    javascript 复制代码
    if (condition) {
      const myModule = require('./module');
      myModule.greet();
    }

3. 运行环境

(1) ESM

  • 浏览器 :ESM 是浏览器的原生模块系统,可以直接在浏览器中使用。

    html 复制代码
    <script type="module" src="app.js"></script>
  • Node.js :从 Node.js 12 开始,ESM 得到了原生支持,但需要使用 .mjs 文件扩展名或在 package.json 中设置 "type": "module"

    json 复制代码
    {
      "type": "module"
    }

(2) CJS

  • Node.js:CJS 是 Node.js 的默认模块系统,广泛用于服务器端开发。
  • 浏览器:CJS 不是浏览器的原生模块系统,需要通过打包工具(如 Webpack、Browserify)转换为浏览器可用的代码。

4. 性能

(1) ESM

  • 静态分析:ESM 支持静态分析,便于工具进行优化(如 Tree Shaking),减少最终打包体积。
  • 异步加载:ESM 支持异步加载模块,适合现代 Web 应用的按需加载需求。

(2) CJS

  • 动态加载:CJS 的动态加载特性使得静态分析和优化变得困难,可能导致打包体积较大。
  • 同步加载:CJS 是同步加载模块的,不适合浏览器环境中的按需加载需求。

5. 互操作性

(1) ESM 导入 CJS

  • 在 ESM 中可以导入 CJS 模块,但需要注意:

    • CJS 模块的默认导出需要通过 default 属性访问。
    javascript 复制代码
    import cjsModule from './cjs-module.js';
    console.log(cjsModule.default);

(2) CJS 导入 ESM

  • 在 CJS 中无法直接导入 ESM 模块(Node.js 中需要使用 import() 动态导入)。

    javascript 复制代码
    import('./esm-module.mjs').then(module => {
      console.log(module.default);
    });

6. 其他差异

(1) 顶层 this

  • ESM :顶层 thisundefined
  • CJS :顶层 thisexports 对象。

(2) 严格模式

  • ESM:默认启用严格模式。
  • CJS :默认不启用严格模式,需要手动添加 "use strict"

(3) 循环依赖

  • ESM:支持循环依赖,但行为与 CJS 不同。
  • CJS:支持循环依赖,但可能导致未完全初始化的模块。

总结对比

特性 ESM(ECMAScript Modules) CJS(CommonJS)
语法 import / export require / module.exports
加载方式 静态加载,支持异步动态加载 动态加载
运行环境 浏览器原生支持,Node.js 12+ 支持 Node.js 默认模块系统
性能 支持静态分析,适合 Tree Shaking 动态加载,难以优化
互操作性 可以导入 CJS 模块 无法直接导入 ESM 模块
顶层 this undefined exports 对象
严格模式 默认启用 默认不启用
循环依赖 支持,行为与 CJS 不同 支持,可能导致未完全初始化的模块

选择建议

  • ESM:适合现代浏览器环境和 Node.js 12+,支持静态分析和异步加载,适合构建现代 Web 应用。
  • CJS:适合 Node.js 环境,尤其是需要动态加载模块的场景。
相关推荐
GuWenyue30 分钟前
不用第三方SDK!Vue3原生Fetch实现DeepSeek流式输出,90%前端都会踩的分片解析坑一次性解决
前端·人工智能·llm
独隅32 分钟前
前端离线暂停更新策略:Service Worker 与 PWA 实战指南
前端
不如摸鱼去1 小时前
Wot UI 2.3.0 发布:二维码组件来了,Open Wot 与 wot-starter 同步更新
前端·ui·微信小程序·前端框架·uni-app
Cloud_bread1 小时前
从ReAct到自主闭环:Agentic Coding核心执行引擎的技术演进
前端·react.js·前端框架
cidy_982 小时前
OptMem 使用教程
前端
杉氧2 小时前
用 Compose 挑战交互与动效天花板:ComposeCraftLab 开源实验室全解析
android·前端·kotlin
Canace2 小时前
AI 生成到 90% 突然断了:你的解决方案是?
前端·人工智能
TinssonTai2 小时前
Vite 8 版 Chrome 插件全家桶,popup/options/sidepanel 一次集齐
前端·vue.js
玉鸯2 小时前
让 Agent 面向用户:AG-UI 协议构建 Agent 前端
前端·python·agent
程序员黑豆2 小时前
鸿蒙应用开发 @Extend 装饰器使用教程
前端·harmonyos