ES Modules(ESM)是JavaScript官方模块标准,由ES2015引入,使用import/export语法,支持静态分析和Tree-shaking;需浏览器原生支持或Node.js≥14.15.1并配置"type":"module",动态导入、顶层await等需额外插件支持。

ES Modules(ESM)是 JavaScript 原生模块标准,但旧版浏览器或某些 Node.js 版本(如低于 14.0 或未启用 --experimental-modules 的早期版本)不完全支持 import/export 语法,尤其当涉及动态导入、顶层 await、裸 specifier(如 import _ from 'lodash')时。Babel 本身不解析或重写模块加载路径,它只转换语法;因此结合 Babel 处理 ESM,关键在于明确目标环境、选择合适 preset/plugin,并配合打包工具或运行时方案。
确认 Babel 转换范围:语法转换 ≠ 模块解析
Babel 默认对 import/export 语句做语法降级(例如转成 CommonJS 的 require/module.exports),但这仅适用于静态模块语句。注意:
- 动态
import()在 Babel 7.8+ 中可通过@babel/plugin-dynamic-import-node(Node 环境)或默认启用的@babel/plugin-syntax-dynamic-import(需配合打包器处理)支持; -
import.meta不会被 Babel 转换,需由打包工具(如 Webpack、Rollup)或运行时注入; - 裸 specifier(
import 'vue')无法被 Babel 解析——路径重写和包定位必须交由打包器或 Node.js 的解析逻辑完成。
配置 Babel 正确处理 ESM 语法
在 .babelrc 或 babel.config.json 中启用对应插件或 preset:
- 使用
@babel/preset-env并设置{"modules": "commonjs"}(默认行为),即可将export default、import * as ns from等转为 CommonJS; - 若需保留 ESM 输出(例如给现代打包器做输入),设
{"modules": false},此时 Babel 只转换语法特性(如箭头函数、解构),不碰import/export; - 启用顶层 await?添加
@babel/plugin-syntax-top-level-await(语法识别),再配合@babel/plugin-transform-top-level-await(转译为 Promise 链); - 注意:Babel 不修改文件扩展名或加载行为,
.mjs文件仍需运行时/打包器按 ESM 规则执行。
必须搭配打包器或运行时方案
纯 Babel 无法解决模块加载问题。常见组合方式:
立即学习“Java免费学习笔记(深入)”;
-
Webpack/Rollup/Vite:将 Babel 作为 loader(如
babel-loader)处理源码,它们负责解析 bare specifiers、tree-shaking、生成 chunk、注入import.meta.url等; -
Node.js(v14.13+):可直接运行
.mjs或type: "module"的package.json,此时 Babel 仅用于转译新语法(如 class fields),模块系统由 Node 原生处理; -
Browser(无打包):需用
<script type="module">,Babel 输出应设modules: false,再靠原生 ESM 加载——此时路径必须为相对/绝对 URL,不能是 npm 包名。
常见陷阱与建议
避免以下典型问题:
- 在
modules: "commonjs"下混用export default和module.exports =手动赋值,会导致导出不一致; - 未禁用打包器的默认 Babel 处理,又重复配置 Babel,引发双重转译(如 import → require → import);
- 期望 Babel 把
import { debounce } from 'lodash'转成const debounce = require('lodash/debounce')——这属于模块解析,Babel 不做,要用 Webpack 的externals或 Rollup 的resolve插件; - 在 Node 中用
require('./index.mjs')加载 ESM 文件会报错,必须用import()动态导入或改用esm包临时兼容。


















