commonjs与esm可共存,需按文件后缀(.mjs/.cjs)和package.json的"type"字段明确模块类型;esm可import cjs但须遵循default或namespace导入规则;cjs调用esm需用动态import()或封装层;避免混用导出、注意__dirname缺失及第三方包exports配置。

CommonJS(CJS)和 ES Module(ESM)混用不是“不能共存”,而是需要明确边界、控制加载时机、适配导出约定。Node.js 自 v12 起已原生支持双模块共存,关键在于配置得当、写法合规、工具辅助。
按文件类型和 package.json 明确模块类型
Node.js 通过静态规则判定模块格式,优先级为:.mjs > .cjs > package.json 的 "type" 字段 > 默认 CJS。
-
.mjs 文件:强制为 ESM,只允许
import/export,require会报错 -
.cjs 文件:强制为 CJS,只允许
require/module.exports,import无效 -
.js 文件:由最近的
package.json中"type": "module"决定;未声明或设为"commonjs"则走 CJS
推荐新项目统一设 "type": "module",遗留 CJS 代码改后缀为 .cjs,避免歧义和意外解析错误。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
在 ESM 中安全导入 CommonJS 模块
ESM 可以 import CJS,但导出映射有固定规则,不能按直觉解构:
- 若 CJS 使用
module.exports = fn,ESM 必须写import fn from './util.cjs'(default 导入) - 若 CJS 使用
exports.helper = () => {}或module.exports.helper = () => {},ESM 应写import * as util from './util.cjs',再用util.helper - 禁止
import { helper } from './util.cjs'—— 因为 CJS 没有命名导出语法,这种写法会返回undefined
在 CommonJS 中调用 ES Module 的可行方案
CJS 的 require() 无法同步加载 ESM,但可通过以下方式绕过限制:
-
动态
import():在async函数中使用const mod = await import('./mod.mjs'),适用于按需加载场景 -
CJS 封装层:新建
wrap.mjs(ESM),再建wrap.cjs(CJS),后者用await import()加载前者并module.exports转发 -
构建工具桥接:Webpack 5+、Vite、Rollup 等能自动识别并标准化混合模块,无需手动处理
__esModule标记或默认导出异常
避开常见陷阱与兼容细节
很多报错其实源于导出写法或环境误判:
- 不要在同一个 CJS 文件里混用
module.exports = xxx和exports.xxx = yyy,后者在前者重赋值后失效 - ESM 中没有
__dirname和__filename,需用import { fileURLToPath } from 'url'手动推导 - 第三方包若同时提供 CJS 和 ESM 版本(如 lodash-es),优先在
package.json中指定"exports"字段引导解析
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










