本文适用于 Node.js 14.x及以上版本,同时覆盖 CommonJS 和 ES Modules 模块系统
在 Node.js 开发中,我们经常需要操作文件:读取配置文件、写入日志文件、加载模板文件等。但很多开发者都会遇到这样的场景:
Error: ENOENT: no such file or directory...
这种错误往往源于错误的路径处理。特别是在不同操作系统(Windows/macOS/Linux)和不同运行环境(本地开发/服务器部署)下,路径处理不当会导致各种兼容性问题。本文将手把手教你 Node.js 路径出路的正确姿势。
__dirname
(CommonJS)const fs = require('fs');
console.log('当前目录:', __dirname);
console.log('同级config文件:',
fs.readFileSync(__dirname + '/config.yaml', 'utf8')); // 注意:实际开发中不要这样拼接路径!
特点:
注意事项:
// app.mjs (需 package.json 设置 "type": "module")
import { fileURLToPath } from 'url';
import { dirname } from 'path';
// 相当于CommonJS的__filename
const currentFileUrl = import.meta.url;
const __filename = fileURLToPath(currentFileUrl);
const __dirname = dirname(__filename);
console.log('ESM目录:', __dirname); // 输出同__dirname
原理拆解:
import.meta.url
:获取当前模块的URL(如:file:///project/src/app.mjs)fileURLToPath()
:转换URL为系统路径(/project/src/app.mjs)dirname()
:提取目录部分(/project/src)process.cwd()
console.log('工作目录:', process.cwd());
// 当通过 node ../src/app.js 运行时
// 输出:/Users/yourname/project(取决于执行命令的位置)
关键点:
__dirname
的区别:process.cwd()
是动态的,__dirname
是静态的特性 | __dirname | ESM 方案 | process.cwd() |
---|---|---|---|
返回值类型 | 绝对路径 | 绝对路径 | 绝对路径 |
是否依赖模块类型 | CommonJS | ES Modules | 通用 |
是否随执行位置变化 | ❌ | ❌ | ✅ |
典型使用场景 | 模块内部路径处理 | ESM项目 | CLI工具开发 |
选择建议:
__dirname
/ ESM 方案处理与文件位置强相关的路径process.cwd()
错误示范:
// Windows下会出错!
const badPath = __dirname + '/../data/file.txt';
// 输出:C:\project/src/../data/file.txt
正确方案:
const path = require('path');
// 安全路径拼接
const goodPath = path.join(__dirname, '..', 'data', 'file.txt');
// 跨平台输出:/project/data/file.txt(POSIX)或 C:\project\data\file.txt(Windows)
// 解析相对路径
const absPath = path.resolve('tmp/log.txt');
// 基于工作目录生成绝对路径
// 获取祖父级目录
const grandParentDir = path.join(__dirname, '../../');
// 获取兄弟目录
const siblingDir = path.join(__dirname, '../shared-module');
console.table({
'__dirname': __dirname,
'process.cwd()': process.cwd(),
'import.meta.url': import.meta.url, // ESM专用
'当前文件': __filename
});
问题复现:
项目结构:
├── src/
│ └── app.js
└── data/
└── input.txt
// 在 app.js 中
fs.readFileSync('../data/input.txt'); // 当在src目录执行时有效
// 但如果通过 node src/app.js 运行就会失败!
解决方案: 始终使用path.join(__dirname, '../data/input.txt')
const isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
const isNode = typeof process !== 'undefined' && process.versions?.node;
console.log('当前环境:', isBrowser ? '浏览器' : isNode ? 'Node.js' : '未知');
const checkPath = (targetPath) => {
try {
fs.accessSync( // 同步检查文件权限的方法
targetPath, // 要检查的目标路径(字符串)
fs.constants.F_OK // 检查标志:文件是否存在
);
return true;
} catch (err) {
if (err.code === 'ENOENT') {
console.error(`路径不存在:${targetPath}`);
return false;
}
throw err;
}
};
// webpack.config.js
module.exports = {
resolve:{
alias:{
'@': path.resolve(__dirname, 'src/') // 配置路径别名
}
}
}
// tsconfig.json
{
"compilerOptions":{
"baseUrl":".",
"paths":{
"@/*":["src/*"]
}
}
}
掌握 Node.js 路径处理的关键要点:
__dirname
(固定位置) 还是 process.cwd()
(动态位置)记住这些黄金法则,你将能游刃有余地处理各种路径问题,写出更健壮的Node.js应用!