在原生 ESM 模块中,import.meta.url 是一个非常有用的属性,它直接返回当前模块文件的 URL。对于浏览器环境,它通常是类似 http://localhost:3000/src/module.js 的完整地址;在 Node.js 的 ESM 模式下,它则返回 file:///path/to/module.js 这样的文件协议 URL。这个属性常被用来构造相对路径、加载 Web Worker、定位静态资源。然而 Webpack 在打包 ESM 代码时,并不会原样保留 import.meta.url 的语义,它会根据输出格式和配置将其替换成运行时计算值。如果开发者不了解这一转换机制,就可能在打包后遇到资源 404、路径解析错误甚至构建警告。

Webpack 对 import.meta.url 的默认处理逻辑
Webpack 从 5 版本开始对 ESM 的支持越来越完善,但默认情况下,output.module 并没有开启,打包结果仍然是普通的 IIFE 或者 CommonJS 风格。此时如果源代码中使用了 import.meta.url,Webpack 会把它当作一个运行时表达式来处理,而不是静态地替换成原文件 URL。具体来说,Webpack 在解析模块时会将 import.meta.url 转换成一个基于 document.baseURI 的字符串。这样做的原因是浏览器中无法在非 ESM 脚本里直接获取当前脚本的 URL,所以只能依赖页面基准地址。
这种默认处理在单页应用里也许能工作,因为 baseURI 通常就是页面地址,配合相对路径可以找到资源。但如果你的脚本被部署到 CDN,或者使用了 publicPath 配置,document.baseURI 与实际模块位置就会产生偏差。比如模块实际地址是 https://cdn.ipipp.com/assets/chunk.js,而页面地址是 https://www.ipipp.com/index.html,那么 import.meta.url 计算出来的值会指向 https://www.ipipp.com/,而不是 CDN 地址。这样依赖 import.meta.url 的代码会全部失效。
另一个常见场景是 Web Worker。很多库使用 new URL('./worker.js', import.meta.url) 来构造 Worker 脚本地址。如果 import.meta.url 被改写成了页面基准地址,而 worker 文件又由 Webpack 单独输出到静态目录,运行时就会请求错误的路径。Webpack 对 new URL 语法有特殊处理,但前提是输出格式为 ESM 或正确配置了 asset module。否则它会保留 new URL 表达式,交由浏览器执行,路径计算仍然依赖被改写的 import.meta.url。
开启 output.module 保留 ESM 语义
要让 Webpack 尽可能保留 import.meta.url 的原生行为,需要同时开启两个配置项:experiments.outputModule 和 output.module。前者用于启用 Webpack 的 ESM 输出实验特性,在 Webpack 5 中虽然标记为实验性,但已经足够稳定;后者将输出的模块格式设置为 ESM。配置如下:
module.exports = {
experiments: {
outputModule: true
},
output: {
module: true,
filename: 'bundle.mjs',
chunkFormat: 'module'
}
};
当 output.module 为 true 时,Webpack 会生成真正的 ES module 文件,并且 import.meta.url 会被保留为原生表达式。浏览器在加载 .mjs 或 type=module 的脚本时会自动提供正确的模块 URL。不过要注意,这种输出格式要求部署环境必须支持 ESM,包括浏览器和 Node.js。如果你的目标环境比较老,或者需要兼容 CommonJS 加载,就不能直接使用 module 输出。
开启 output.module 后,chunkFormat 默认会变为 module,但建议显式声明 chunkFormat 为 module,以避免 Webpack 在动态导入场景下产生混合格式的 chunk。还需要注意,ESM 输出不能与某些 CommonJS 特有的功能共存,例如 __dirname 和 __filename 在 ESM 中不存在,Webpack 会提供自己的运行时变量,但这些变量在严格 ESM 输出中可能不可用。对于 Node.js 项目,如果 package.json 设置了 type: module,Webpack 的 ESM 输出可以直接被 import 导入。
在实际项目中,如果你的源码大量使用 import.meta.url 来获取当前模块路径,建议将构建产物输出为 ESM。这样浏览器和 Node.js 都能按标准语义解析路径,减少后续转换成本。但仅开启 output.module 并不能解决所有问题,因为 import.meta.url 返回的是 URL 格式,而很多 Node.js API 和 webpack 插件期望文件系统路径,此时需要借助 Node.js 的 fileURLToPath 进行转换。
从 URL 到文件系统路径:fileURLToPath 与 new URL 的配合
在 Node.js ESM 环境中,import.meta.url 返回的是 file:/// 开头的 URL,而很多原生模块函数如 fs.readFileSync 需要正常的绝对路径。Node.js 提供了 url.fileURLToPath 方法来完成这个转换。例如:
import { fileURLToPath } from 'url';
import { readFileSync } from 'fs';
const currentPath = fileURLToPath(import.meta.url);
console.log(currentPath); // /home/user/project/src/module.mjs
Webpack 在打包 Node.js 目标代码时,如果你开启了 ESM 输出,import.meta.url 会保留,但 fs 和 url 模块通常会被 external 或打进来。对于 Node.js 目标,建议将 node 内置模块配置为 external,避免 Webpack 尝试打包它们。fileURLToPath 在运行时由 Node.js 提供,不会被 Webpack 静态替换,因此可以正常工作。不过如果代码需要在浏览器中运行,fileURLToPath 不可用,这时必须使用 URL 对象来处理路径。
浏览器场景下推荐使用 new URL(relativePath, import.meta.url) 来构造资源地址。Webpack 对这个语法有原生支持:当它检测到 new URL 的第一个参数是字符串或表达式,第二个参数是 import.meta.url 时,会将相关文件作为资源模块处理。例如:
const workerUrl = new URL('./worker.js', import.meta.url);
const worker = new Worker(workerUrl, { type: 'module' });
Webpack 会把 ./worker.js 识别为资源依赖,并根据 output.assetModuleFilename 或 asset 模块规则输出文件,同时把 URL 替换成最终的公共路径。这个机制不依赖 output.module 是否开启,但在 ESM 输出下行为更可预测。如果你使用 asset module 类型,还可以进一步配置文件名:
module.exports = {
output: {
assetModuleFilename: 'assets/[name][ext]'
},
module: {
rules: [
{
test: /\.worker\.js$/,
type: 'asset/resource'
}
]
}
};
这样 worker 文件会被单独输出到 assets 目录,new URL 的返回值也会指向该目录。需要注意的是,如果 import.meta.url 被 Webpack 改写成 document.baseURI,new URL 的第一个参数如果是相对路径,会基于错误的基础地址解析。因此对于任何依赖 import.meta.url 的资源加载,开启 output.module 或者使用 asset module 是保证路径正确的前提。
常见路径错误与打包后的调试方法
在迁移到 ESM 输出或引入 import.meta.url 时,开发者最常遇到的报错是构建阶段的警告:Critical dependency: the request of a dependency is an expression。这个警告通常出现在 import.meta.url 被当作动态表达式使用时,Webpack 无法静态分析依赖。例如:
const file = './images/' + name + '.png'; const url = new URL(file, import.meta.url);
这里第一个参数不是静态字符串,Webpack 无法确定具体文件,只能发出警告并保留运行时表达式。解决办法是将静态部分提取出来,或者使用 require.context 类似的机制。对于 ESM 代码,可以改用 import 语句来确保静态分析:
import iconUrl from './images/icon.png';
另一个问题是打包后的文件中 import.meta.url 变成了 undefined。这通常是因为没有开启 ESM 输出,而浏览器又不支持 document.baseURI 之外的解析逻辑。你可以通过查看打包产物中 import.meta.url 出现的位置来确认。如果产物不是 .mjs 文件,或者 script 标签没有 type=module,浏览器不会提供 import.meta 对象,访问 import.meta.url 就会报错。此时要么改用 ESM 输出,要么避免在运行时直接使用 import.meta.url,而是通过构建时注入的 __webpack_public_path__ 或者 publicPath 来拼接路径。
还有一个容易混淆的点是 publicPath 与 import.meta.url 的关系。publicPath 决定 Webpack 在运行时加载 chunk 和资源的基础路径,它默认是空字符串或者 auto。import.meta.url 则在原生 ESM 中指向当前模块真实地址。两者并不总是相同。如果你的服务端渲染页面与静态资源分离,建议将 publicPath 显式设置为 CDN 地址,同时将 import.meta.url 用于模块内相对路径的构造。在开启 output.module 后,Webpack 会尽量让 import.meta.url 与 publicPath 保持一致,但这并不是强制的。
最后,Node.js 目标下如果 package.json 没有 type: module,Webpack 输出 .js 文件会被当作 CommonJS,import.meta 会直接抛出 SyntaxError。因此在 Node.js ESM 项目中,务必确认输出文件名后缀为 .mjs,或者在 package.json 中声明 type: module。Webpack 的 output.filename 可以设置为 [name].mjs,同时将 chunkFilename 也改为 .mjs 后缀。这样可以避免大多数运行时兼容性问题。
Webpackimport.meta.urlESM修改时间:2026-10-07 02:01:36