es module 迁移需解决 commonjs 的 exports/module.exports 语义断层:前者是初始别名,后者可重赋值;esm 的 export 是静态声明、不可覆盖。应按导出意图分场景处理——单个值用 export default,命名属性用具名 export,混合导出需重构;混用时注意 webpack/node.js 的兼容包装机制及 import 语法限制。

迁移到 ES Module 时,module.exports 和 exports 的兼容性问题本质是语义断层:CommonJS 中 exports 只是 module.exports 的初始引用别名,而 ES Module 的 export 是静态声明、不可赋值覆盖的。直接替换语法会出错,关键在理解差异并分场景处理。
明确 exports 和 module.exports 的关系再动刀
CommonJS 中这两者不是等价的:
-
exports.xxx = ...等价于module.exports.xxx = ...,前提是module.exports还是原始空对象 - 一旦你写了
module.exports = {...}或module.exports = function() {...},exports就失效了——它不再指向module.exports - ES Module 没有“赋值导出”概念,
export default和export const xxx都是声明式,不能后期覆盖
按原 CommonJS 导出模式逐类迁移
不要统一替换成 export default,要根据原有导出意图选择:
-
单个函数/类/对象导出(如
module.exports = class X {})→ 用export default
对应导入从const X = require('./x')改为import X from './x.js' -
命名属性导出(如
exports.add = () => {}或module.exports = { add, sub })→ 用具名export
写成export const add = () => {};导入从const { add } = require('./math')改为import { add } from './math.js' -
混合导出(既有默认又有命名,如先
module.exports = function()再exports.helper = ...)→ 必须拆开或重构
ESM 不支持这种模式。建议:主逻辑用export default,辅助工具提成独立具名导出,或封装进默认导出对象里
Webpack 或 Node.js 环境下混用需加运行时适配
如果项目尚未全量切换,部分文件仍是 CommonJS,而你用 ES Module 导入它们,要注意:
- Webpack 5+ 会自动包装 CJS 模块为 ESM 兼容形式,但
require('./cjs-file')导出的对象会被挂到default属性上
→ 所以import utils from './utils.cjs'可以,但import { helper } from './utils.cjs'会失败,除非该 CJS 文件用了module.exports = { helper }且你启用了 Webpack 的exports-loader或resolve.fullySpecified: true - Node.js 原生 ESM 中,导入 CommonJS 模块返回的是一个带有
default属性的命名空间对象
→ 若原 CJS 是module.exports = { a: 1 },ESM 中需写import mod from './cjs.js'; console.log(mod.default.a),或用createRequire回退到 require
避免常见陷阱
这些操作在 ESM 下非法,迁移时务必检查:
- 删除所有
exports.xxx =或module.exports =赋值语句,改用export声明 - 禁止在 if / for / 函数内写
export—— ESM 导出必须在顶层 - 动态路径
require(somePath)无法直接转为import,需用import()动态导入(返回 Promise) - 注意文件扩展名:Node.js 中 ESM 文件推荐用
.mjs或package.json中设"type": "module",否则仍按 CJS 解析
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











