Webpack 5 的 output.module 配置在很多项目中用来控制生成文件的模块格式,但它并不等同于把一个应用打包成一个完全脱离 Webpack 运行时的原生 ES 模块文件。理解这个选项的关键,是先区分模块语法和模块加载机制这两个层面。

output.module 为 true 时,Webpack 生成的 JavaScript 确实会使用 import 和 export 语法,而不再是 require 和 module.exports。但这个文件仍然由 Webpack 的 runtime 驱动,模块注册、依赖收集和缓存逻辑依旧存在。也就是说,它输出的是面向原生 ESM 环境的 Webpack bundle,而不是把每个源码模块单独编译成浏览器可直接运行的独立原生模块。
output.module 的实际作用边界
要判断一个构建产物是不是真正的原生 ES 模块,通常会看两点:文件是否使用标准的 ESM 语法,以及文件是否能被浏览器或 Node.js 不经过额外加载器直接解析。前者是语法层面,后者是运行时层面。
output.module 解决的主要是第一个层面。开启后,Webpack 会把生成的模块包装代码从 CommonJS 风格切换为 ESM 风格。原来常见的 __webpack_modules__[id] = function(module) { ... } 这类基于 require 的模块定义会被改写为通过 __webpack_require__.d 和 __webpack_require__.r 标记导出对象的 ESM 风格代码。虽然内部仍保留 __webpack_require__ 这样的函数名,但这些函数体的组织方式已经兼容 ES module 的目标环境。
从配置角度看,Webpack 5 中通常需要同时开启实验特性或直接设置模块输出,示例配置如下:
module.exports = {
experiments: {
outputModule: true,
},
output: {
module: true,
filename: 'main.mjs',
},
target: ['web', 'es2020'],
};
这里使用 filename: 'main.mjs' 并不是强制要求,但如果部署在浏览器环境,.mjs 后缀可以帮助服务器返回正确的 JavaScript 模块 MIME 类型。同时 target 中的 es2020 表示让 Webpack 生成更接近现代浏览器原生语法的代码,避免为旧环境填充过多降级逻辑。
开启前后的产物差异
下面通过一个极简示例观察模块格式变化。假设源码包含一个数学工具模块和一个入口文件:
// src/math.js
export function add(a, b) {
return a + b;
}
// src/index.js
import { add } from './math';
console.log(add(1, 2));
在默认 CommonJS 输出模式下,Webpack 生成的 bundle 通常是自执行的立即调用函数表达式,内部使用 require 风格完成模块加载。简化后的产物可能类似:
(() => {
var __webpack_exports__ = {};
function add(a, b) {
return a + b;
}
console.log(add(1, 2));
})();
开启 output.module 后,Webpack 会改用 ESM 语义来组织模块定义和导出。简化的输出片段可能像下面这样:
var __webpack_modules__ = {
'./src/math.js': (__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
__webpack_require__.d(__webpack_exports__, {
add: () => add
});
function add(a, b) {
return a + b;
}
}
};
可以看到,虽然模块包装仍然存在,但导出行为已经不再依赖 module.exports 对象,而是通过 __webpack_require__.d 在
webpackoutput_moduleES模块修改时间:2026-08-13 05:01:45