path 路径模块
🎯 引言
学完这篇文章,你能用 path 模块正确拼接和解析文件路径,说清楚 __dirname 和 process.cwd() 的区别,从此不再被“文件明明存在却读取失败”的问题困扰。
🧱 为什么需要 path 模块
不同操作系统的路径写法不一样:Windows 用反斜杠 \,macOS 和 Linux 用正斜杠 /。如果用字符串硬拼路径:
// 在 Windows 上可能出问题,换个系统就失效
const file = './logs' + '/' + 'app.log';
path 是 Node.js 内置的路径处理模块,它会根据当前系统自动选择正确的分隔符,还能帮我们解析路径的各个部分。引入方式:
import path from 'node:path';
🛠 拼接路径:join 与 resolve
path.join 把多段路径拼成一个,自动处理分隔符和多余的斜杠:
import path from 'node:path';
const logFile = path.join('./logs', '2024', 'app.log');
console.log(logFile); // logs/2024/app.log(Windows 上为 logs\2024\app.log)
path.resolve 则会基于当前工作目录计算出完整的绝对路径:
import path from 'node:path';
const fullPath = path.resolve('./logs', 'app.log');
console.log(fullPath); // /Users/你/项目目录/logs/app.log
join 只做拼接,结果是相对的还是绝对的取决于你传入的内容;resolve 一定会返回绝对路径。项目里定位文件通常用 resolve 更稳妥。🛠 解析路径:basename、dirname、extname、parse
拿到一个路径字符串,想拆出其中的某部分,用这组方法:
import path from 'node:path';
const file = '/user/local/demo/index.html';
console.log(path.basename(file)); // 文件名:index.html
console.log(path.dirname(file)); // 所在目录:/user/local/demo
console.log(path.extname(file)); // 扩展名:.html
console.log(path.parse(file));
// { root: '/', dir: '/user/local/demo', base: 'index.html', ext: '.html', name: 'index' }
parse 一次拆出所有部分,适合需要同时用到好几样的场景。
⚡ __dirname 与 process.cwd() 的区别
这是初学者很容易混淆的两个值:
__dirname:当前这个 js 文件所在的目录,是固定不变的。process.cwd():你在终端哪个目录下执行的 node 命令,跟着你的执行位置变。
做个小实验,在项目根目录下创建 demo/show.js:
console.log('__dirname:', __dirname);
console.log('cwd:', process.cwd());
在项目根目录执行 node demo/show.js,输出类似:
__dirname: /Users/你/project/demo
cwd: /Users/你/project
文件在 demo 目录里,但你站在根目录执行,两个值就不一样了。
process.cwd(),而不是当前文件的位置。在根目录执行 node demo/show.js,代码里的 ./data.txt 会去根目录找,而不是 demo 目录。想稳定定位文件,用 path.join(__dirname, 'data.txt')。💡 ESM 中没有 __dirname
前面模块化一篇提过,ES Module 里没有 __dirname。Node.js 20.11 之后可以直接用 import.meta 上的两个属性:
console.log('当前文件所在目录:', import.meta.dirname);
console.log('当前文件完整路径:', import.meta.filename);
有了 import.meta.dirname,拼路径的方式和 CommonJS 完全一样:
import path from 'node:path';
const dataFile = path.join(import.meta.dirname, 'data.txt');
🧾 小节总结
- path 模块按当前系统自动处理路径分隔符,避免手写字符串拼接的兼容问题。
join只负责拼接,resolve会基于工作目录返回绝对路径。basename、dirname、extname、parse用于拆解路径的各个部分。__dirname是文件所在目录,process.cwd()是执行 node 命令时所在的目录。- 相对路径读写文件相对于
cwd,定位文件建议用path.join(__dirname, ...);ESM 中用import.meta.dirname。
❓ 知识问答
Q1:join 和 resolve 到底用哪个?
想拼出绝对路径定位项目文件,用 resolve 或 join(__dirname, ...);只是想简单拼几段路径字符串,用 join。两者都不会校验路径是否真实存在。
Q2:为什么我在根目录执行脚本,相对路径就找不到文件了?
因为相对路径相对于 process.cwd()(你执行命令的位置),而不是文件自身位置。改用 path.join(__dirname, '文件名') 就能解决。
Q3:ESM 里想用 __dirname 怎么办?
Node.js 20.11 之后直接用 import.meta.dirname。更老的版本需要通过 import.meta.url 配合 fileURLToPath 转换,升级 Node 版本后就不需要了。
Q4:path 模块能判断路径存不存在吗?
不能,path 只负责处理字符串。判断文件是否存在要用 fs 模块,比如 fs.existsSync 或 try/catch。
🧪 小练习
假设你的脚本位于 project/tools/clean.js,要求:无论用户在哪个目录下执行 node tools/clean.js,都能正确拼出 project/logs/app.log 的绝对路径并打印。
import path from 'node:path';
// 请在这里编写代码
🎉 恭喜你已经掌握路径处理的核心知识!下一篇我们用 http 模块搭建一个真正的 Web 服务器。
