导读:本期聚焦于小伙伴创作的《JavaScript模块化发展历程中CommonJS与ESM如何共存?》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《JavaScript模块化发展历程中CommonJS与ESM如何共存?》有用,将其分享出去将是对创作者最好的鼓励。

JavaScript模块化发展过程中,CommonJS和ESM是两种应用最广泛的模块规范,前者是Node.js早期默认的模块方案,后者是ECMAScript官方标准模块规范。很多存量项目基于CommonJS开发,新功能又倾向于使用ESM,因此两者共存成为开发中的常见需求。

JavaScript模块化发展历程中CommonJS与ESM如何共存?

CommonJS与ESM的核心差异

要实现两者共存,首先需要明确两种规范的核心区别,避免因为特性差异导致兼容问题。

  • 加载方式:CommonJS是运行时同步加载,ESM是编译时静态分析异步加载
  • 导出方式:CommonJS通过module.exports导出,ESM通过export关键字导出
  • 导入方式:CommonJS使用require导入,ESM使用import关键字导入
  • 作用域:CommonJS模块有独立的moduleexportsrequire全局变量,ESM模块默认严格模式,没有这些顶层变量

Node.js环境下的共存方案

1. 通过package.json的type字段区分模块类型

Node.js从12.17.0版本开始正式支持ESM,默认情况下.js文件会被识别为CommonJS模块,若要在项目中同时使用两种规范,可以通过package.jsontype字段设置默认模块类型,再通过文件扩展名区分不同规范的模块。

type设置为module时,默认.js文件为ESM模块,CommonJS模块需要使用.cjs扩展名;当type未设置或设置为commonjs时,默认.js文件为CommonJS模块,ESM模块需要使用.mjs扩展名。

示例package.json配置:

{
  "name": "module-demo",
  "type": "module",
  "version": "1.0.0"
}

此时项目中的utils.mjs是CommonJS模块,index.js是ESM模块,两者可以互相导入:

// utils.cjs  CommonJS模块
const add = (a, b) => a + b;
module.exports = { add };
// index.js  ESM模块
import { add } from './utils.cjs';
console.log(add(1, 2)); // 输出3

2. 使用动态import加载CommonJS模块

在ESM模块中,无法直接用静态import导入CommonJS模块,需要使用动态import()方法,该方法返回Promise,可以异步获取CommonJS模块的导出内容。

// ESM模块中导入CommonJS模块
const loadCommonJSModule = async () => {
  const commonjsModule = await import('./utils.cjs');
  console.log(commonjsModule.add(2, 3)); // 输出5
};
loadCommonJSModule();

3. 包导出字段配置兼容两种规范

如果是开发npm包,需要在package.json中配置exports字段,分别指定CommonJS和ESM的导出路径,让使用不同规范的项目都能正确导入。

{
  "name": "my-utils",
  "type": "module",
  "exports": {
    ".": {
      "import": "./dist/esm/index.js",
      "require": "./dist/cjs/index.cjs"
    }
  },
  "main": "./dist/cjs/index.cjs",
  "module": "./dist/esm/index.js"
}

上述配置中,import字段指定ESM项目的导入路径,require字段指定CommonJS项目的导入路径,mainmodule是兼容旧版工具的配置。

前端构建工具中的共存方案

1. Webpack中的处理

Webpack默认支持两种模块的解析,只需要在配置中明确模块解析规则即可。如果项目中同时存在两种规范的模块,Webpack会自动将ESM转换为CommonJS,或者根据target配置输出对应规范的代码。

// webpack.config.js
module.exports = {
  entry: './src/index.js',
  output: {
    filename: 'bundle.js',
    path: require('path').resolve(__dirname, 'dist')
  },
  resolve: {
    extensions: ['.js', '.cjs', '.mjs']
  }
};

2. Vite中的处理

Vite默认优先使用ESM,对于CommonJS模块会在预构建阶段将其转换为ESM。如果项目中需要同时支持两种规范,只需要正常导入即可,Vite会自动处理兼容问题。

// Vite项目中导入CommonJS模块
const commonjsLib = require('some-commonjs-lib');
// 也可以混合使用ESM导入
import { esmFunc } from './esm-module.js';

共存时的注意事项

  • CommonJS的__dirname__filename在ESM中不存在,需要使用import.meta.url配合path模块获取等效路径
  • CommonJS的循环依赖处理方式与ESM不同,共存时尽量避免复杂的循环依赖场景
  • 部分旧版工具不支持.mjs.cjs扩展名,需要提前确认工具链的兼容性
  • 动态导入返回的是模块命名空间对象,CommonJS的默认导出会被挂载到default属性上,需要注意取值方式
合理处理CommonJS和ESM的共存,既能复用存量CommonJS代码,又能享受ESM静态分析、 tree-shaking等优势,是过渡阶段项目的最优选择。

CommonJSESMJavaScript_模块化模块互操作修改时间:2026-07-06 00:21:29

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。