
ES 模块中无法直接复用 __dirname,因其依赖 import.meta.url 的上下文;正确做法是导出一个接收 import.meta.url 的函数,在每个模块内调用以获得该模块自身的目录路径。
es 模块中无法直接复用 `__dirname`,因其依赖 `import.meta.url` 的上下文;正确做法是导出一个接收 `import.meta.url` 的函数,在每个模块内调用以获得该模块自身的目录路径。
在 CommonJS 中,__dirname 是一个内置变量,天然指向当前模块所在目录;但在 ES 模块(.mjs 或 type: "module" 的 .js)中,__dirname 不可用,必须通过 import.meta.url + fileURLToPath() + dirname() 手动构造。关键在于:import.meta.url 总是相对于当前模块的路径,因此任何封装成独立工具模块(如 utils/__dirname.js)的做法都会导致 __dirname 固定指向该工具模块自身所在的目录(如 src/utils),而非调用它的模块(如 app.js 所在的 src/)。
✅ 正确方案:函数式 makeDirname()
将路径解析逻辑封装为纯函数,要求调用方显式传入自身的 import.meta.url:
// dirname.mjs
import { dirname } from 'path';
import { fileURLToPath } from 'url';
export function makeDirname(metaUrl) {
return dirname(fileURLToPath(metaUrl));
}在每个需要 __dirname 的模块中调用它:
// app.js
import { makeDirname } from './dirname.mjs';
const __dirname = makeDirname(import.meta.url);
console.log(__dirname); // → 输出 app.js 所在的绝对路径,例如 /project/src? 为什么不能直接 export const __dirname = ...?
因为 import.meta.url 在 dirname.mjs 内部解析时,永远是 dirname.mjs 自身的 URL,与谁导入它无关——模块作用域是静态的,无法动态捕获调用栈。
⚠️ 注意事项
- 必须在每个使用 __dirname 的文件中单独声明(如 const __dirname = makeDirname(import.meta.url)),无法“一键全局注入”;
- 确保文件扩展名为 .mjs 或 package.json 中已设置 "type": "module",否则 import.meta.url 不可用;
- 若需兼容 TypeScript,可为 makeDirname 添加类型声明:
export function makeDirname(metaUrl: import('url').URL): string;
✅ 替代方案(仅限 Node.js ≥ 20.6+)
Node.js 20.6+ 提供了实验性 API import.meta.dirname(需启用 --experimental-import-meta-resolve),未来可能成为标准解法:
// Node.js 20.6+(稳定后推荐) const __dirname = import.meta.dirname;
但现阶段仍应优先使用 makeDirname(import.meta.url) 方案,兼顾兼容性与语义清晰性。


















