Babel配置中如何同时支持ES模块和CommonJS?

司徒斐然 阅读 19

我在升级项目Babel配置时遇到了问题。之前用@babel/preset-env默认配置没问题,但今天想让代码同时兼容ES模块和CommonJS时,打包后出现Unexpected token 'export'错误。尝试在.babelrc里加了


{
  "presets": [
    ["@babel/preset-env", {
      "targets": { "node": "12" },
      "modules": false
    }]
  ]
}

但问题依旧。如果把modules设为false应该保留原生模块,那为什么在Node环境还是会报错?有没有同时支持两种模块系统的正确配置方式?

我来解答 赞 1 收藏
二维码
手机扫码查看
1 条解答
昊然 ☘︎
这个问题其实挺常见的,很多人在用 Babel 配置时会遇到类似的坑。具体来说,问题的核心在于如何让 Babel 同时支持 ES 模块和 CommonJS 模块,尤其是在 Node.js 环境下。

首先我们得明确一点,Node.js 对 ES 模块的支持是有条件的。默认情况下,Node.js 使用 CommonJS 模块系统,但如果你的代码中使用了 exportimport,它会尝试解析为 ES 模块。然而,Babel 的 @babel/preset-env 默认不会帮你处理这种混合模块的情况,尤其是当你的目标环境是 Node.js 时。

解决方案

要同时支持 ES 模块和 CommonJS,我们需要调整 Babel 的配置,并且确保打包工具(比如 Webpack、Rollup 或者直接运行 Node.js)能够正确识别模块类型。以下是分步骤的解决方案:

第一步:调整 Babel 配置
我们需要修改 .babelrc 文件,让它既能保留 ES 模块语法,又能转译成 CommonJS。关键在于 modules 配置项。你之前设置为 false 是为了保留原生 ES 模块语法,但这会导致 Node.js 无法正确解析 exportimport。解决办法是动态决定模块格式。

{
"presets": [
["@babel/preset-env", {
"targets": { "node": "12" },
"modules": process.env.BABEL_MODULES || false
}]
]
}


这里的关键点是 process.env.BABEL_MODULES。通过这种方式,我们可以在不同环境下动态切换模块格式。例如,在开发环境下保留 ES 模块,在构建时转译为 CommonJS。

第二步:在构建工具中控制模块格式
如果你使用的是 Webpack 或 Rollup 这样的打包工具,可以通过环境变量来控制模块格式。比如,在 Webpack 中可以这样配置:

const webpack = require('webpack');

module.exports = (env, argv) => {
const isProduction = argv.mode === 'production';
return {
// 其他配置...
plugins: [
new webpack.DefinePlugin({
'process.env.BABEL_MODULES': JSON.stringify(isProduction ? 'commonjs' : false)
})
]
};
};


这段代码的作用是,当运行生产环境构建时,Babel 会将模块格式转译为 CommonJS;而在开发环境下,则保留 ES 模块语法。

第三步:确保 Node.js 正确解析模块
如果你直接在 Node.js 环境下运行代码,还需要确保文件扩展名和 package.json 的配置正确。具体来说:

1. 如果你想让某个文件以 ES 模块方式运行,需要在 package.json 中添加 "type": "module"
2. 如果某些文件需要以 CommonJS 方式运行,可以显式使用 .cjs 扩展名。

例如:

{
"type": "module"
}


或者对于特定文件,你可以这样写:

// index.cjs
module.exports = { foo: 'bar' };

// index.mjs
export const foo = 'bar';


第四步:验证配置是否生效
最后一步是验证。你可以通过以下方式测试:

1. 在开发环境中运行代码,检查是否保留了 importexport 语法。
2. 在生产环境中运行代码,检查是否正确转译为 CommonJS。

举个例子,假设你有一个简单的模块文件:

// src/module.js
export const hello = () => console.log('Hello, world!');


打包后应该变成类似这样的 CommonJS 代码:

// dist/module.js
const hello = () => console.log('Hello, world!');
exports.hello = hello;


总结一下
这个解决方案的核心在于动态控制 Babel 的 modules 配置,结合环境变量和构建工具的特性,让代码在不同环境下都能正确运行。需要注意的是,Node.js 的模块解析机制对文件扩展名和 package.json 配置非常敏感,所以一定要确保这些细节没有遗漏。

说实话,这种模块兼容性的问题确实让人头大,但只要理清楚原理,按照步骤来,还是能搞定的。希望这个回答能帮到你!
点赞 5
2026-02-14 13:24