Node.js: path 与 url 模块

最后更新:2026-08-26

1. 1 故事:一场分隔符引发的部署灾难

Bob 开发的 CLI 工具在 macOS 上测试一切正常——路径拼接用 /,读取配置、写入日志都没问题。然而部署到 Linux 服务器后,程序立刻报错:ENOENT: no such file or directory。排查后发现,Bob 在代码中硬编码了 / 作为路径分隔符,而某些路径处理逻辑在 Windows 上使用了 \,导致路径解析混乱。这次事故让 Bob 深刻理解了 path 模块存在的意义——永远不要手动拼接路径。

(1) 你将学到



2. 2 path 模块核心方法

path 模块是 Node.js 内置模块,提供文件路径的拼接、解析、格式化等工具,自动处理不同操作系统的路径分隔符差异。

(1) path.join —— 路径拼接

path.join() 将多个路径片段拼接为一个标准化路径,自动使用当前系统的分隔符。

▶ 示例:path.join 基础拼接

JAVASCRIPT
const path = require('path');

const fullPath = path.join('/app', 'src', 'utils', 'helper.js');
console.log(fullPath);
// macOS/Linux: /app/src/utils/helper.js
// Windows:     \app\src\utils\helper.js
▶ 试一试

▶ 示例:path.join 自动规范化

JAVASCRIPT
const path = require('path');

console.log(path.join('/app', '../config', 'settings.json'));
// /config/settings.json

console.log(path.join('src', '.', 'index.js'));
// src/index.js
▶ 试一试

(2) path.resolve —— 解析为绝对路径

path.resolve() 从右向左拼接路径,直到得到一个绝对路径。若未得到绝对路径,则以当前工作目录为基准。

▶ 示例:path.resolve 基础用法

JAVASCRIPT
const path = require('path');

console.log(path.resolve('src', 'index.js'));
// /current/working/dir/src/index.js

console.log(path.resolve('/app', 'src', 'index.js'));
// /app/src/index.js

console.log(path.resolve('/app', '/tmp', 'file.txt'));
// /tmp/file.txt(以最右侧绝对路径为准)
▶ 试一试

(3) path.parse 与 path.format —— 路径解构与重组

path.parse() 将路径拆解为 root、dir、base、ext、name 五个部分;path.format() 则将对象重组为路径字符串。

▶ 示例:path.parse 解构路径

JAVASCRIPT
const path = require('path');

const parsed = path.parse('/app/src/utils/helper.js');
console.log(parsed);
▶ 试一试
TEXT 📖 仅展示
{
  root: '/',
  dir: '/app/src/utils',
  base: 'helper.js',
  ext: '.js',
  name: 'helper'
}
100%
graph LR
    A["/app/src/utils/helper.js"] --> B["root: /"]
    A --> C["dir: /app/src/utils"]
    A --> D["base: helper.js"]
    D --> E["name: helper"]
    D --> F["ext: .js"]
    style A fill:#4CAF50,color:#fff
    style B fill:#FF9800,color:#fff
    style C fill:#2196F3,color:#fff
    style D fill:#9C27B0,color:#fff
    style E fill:#E91E63,color:#fff
    style F fill:#FF5722,color:#fff

▶ 示例:path.format 重组路径

JAVASCRIPT
const path = require('path');

const filePath = path.format({
  dir: '/app/src/utils',
  base: 'helper.js'
});
console.log(filePath);
// /app/src/utils/helper.js
▶ 试一试

(4) path.extname / path.basename / path.dirname

这三个方法分别提取路径的扩展名、文件名和目录部分。

▶ 示例:提取路径各部分

JAVASCRIPT
const path = require('path');

const filePath = '/app/src/utils/helper.js';

console.log(path.extname(filePath));   // .js
console.log(path.basename(filePath));  // helper.js
console.log(path.basename(filePath, '.js')); // helper
console.log(path.dirname(filePath));   // /app/src/utils
▶ 试一试

3. 3 path 跨平台常量

不同操作系统的路径分隔符和环境变量分隔符不同,path 模块提供常量来适配。

(1) path.sep —— 路径分隔符

平台 path.sep
macOS / Linux /
Windows \

(2) path.delimiter —— 环境变量分隔符

平台 path.delimiter
macOS / Linux :
Windows ;

▶ 示例:使用 path.sep 和 path.delimiter

JAVASCRIPT
const path = require('path');

console.log('分隔符:', JSON.stringify(path.sep));
// macOS/Linux: "/"
// Windows:     "\\"

const envPaths = process.env.PATH.split(path.delimiter);
console.log('PATH 条目数:', envPaths.length);
▶ 试一试

4. 4 url 模块与 URL 构造函数

(1) url.parse(已废弃)vs new URL()

url.parse() 是旧版 API,已标记为废弃,推荐使用 WHATWG 标准的 new URL() 构造函数。

特性 url.parse() new URL()
标准 Node.js 旧版 WHATWG 标准
状态 已废弃 推荐
错误处理 静默返回 null 抛出 TypeError
searchParams 内置 URLSearchParams
性能 较慢 较快

▶ 示例:url.parse 旧版用法(不推荐)

JAVASCRIPT
const url = require('url');

const parsed = url.parse('https://example.com/api/users?name=Bob&page=1');
console.log(parsed.hostname);
console.log(parsed.query);
▶ 试一试
TEXT 📖 仅展示
example.com
name=Bob&page=1

▶ 示例:new URL() 推荐用法

JAVASCRIPT
const myUrl = new URL('https://example.com/api/users?name=Bob&page=1');

console.log(myUrl.hostname);
console.log(myUrl.pathname);
console.log(myUrl.searchParams.get('name'));
console.log(myUrl.searchParams.get('page'));
▶ 试一试
TEXT 📖 仅展示
example.com
/api/users
Bob
1

(2) url.searchParams —— 查询参数操作

URL 对象的 searchParams 属性是一个 URLSearchParams 实例,提供便捷的查询参数增删改查。

▶ 示例:searchParams 增删改查

JAVASCRIPT
const myUrl = new URL('https://example.com/search');
myUrl.searchParams.set('q', 'nodejs');
myUrl.searchParams.set('lang', 'zh');
myUrl.searchParams.append('tag', 'backend');
myUrl.searchParams.append('tag', 'tutorial');
myUrl.searchParams.delete('lang');

console.log(myUrl.toString());
// https://example.com/search?q=nodejs&tag=backend&tag=tutorial

console.log(myUrl.searchParams.getAll('tag'));
// [ 'backend', 'tutorial' ]
▶ 试一试

(3) url.fileURLToPath —— file URL 转本地路径

在 ESM 模块中,import.meta.url 返回的是 file:// 协议的 URL,需要用 url.fileURLToPath() 转为文件系统路径。

▶ 示例:fileURLToPath 转换

JAVASCRIPT
const { fileURLToPath } = require('url');

const fileUrl = 'file:///app/src/index.js';
const filePath = fileURLToPath(fileUrl);
console.log(filePath);
// macOS/Linux: /app/src/index.js
// Windows:     \app\src\index.js
▶ 试一试

5. 5 __dirname / __filename vs import.meta.url

这是 CJS 与 ESM 模块系统中获取当前文件路径的核心差异。

特性 __dirname / __filename import.meta.url
模块系统 CJS(require) ESM(import)
返回类型 绝对路径字符串 file:// URL 字符串
可用性 全局变量,直接使用 需配合 fileURLToPath
目录路径 __dirname 直接获取 需 dirname(fileURLToPath())
文件路径 __filename 直接获取 需 fileURLToPath() 转换

▶ 示例:CJS 中使用 __dirname

JAVASCRIPT
const path = require('path');

console.log('__dirname:', __dirname);
console.log('__filename:', __filename);

const configPath = path.join(__dirname, 'config', 'settings.json');
console.log(configPath);
▶ 试一试

▶ 示例:ESM 中使用 import.meta.url

JAVASCRIPT
import { fileURLToPath } from 'url';
import path from 'path';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

console.log('__dirname:', __dirname);
console.log('__filename:', __filename);
▶ 试一试

6. 6 path 常用方法速查表

方法 参数 返回值 用途
path.join() ...paths string 拼接路径片段,自动规范化
path.resolve() ...paths string 解析为绝对路径
path.parse() pathString object 解构路径为各组成部分
path.format() pathObject string 将路径对象重组为字符串
path.extname() pathString string 获取文件扩展名
path.basename() pathString[, ext] string 获取文件名(可去掉扩展名)
path.dirname() pathString string 获取目录部分
path.isAbsolute() pathString boolean 判断是否为绝对路径
path.normalize() pathString string 规范化路径(处理 ...
path.relative() from, to string 获取从 from 到 to 的相对路径


7. 7 跨平台路径处理最佳实践

(1) 核心原则

规则 说明 反面示例 正面示例
用 path.join 自动处理分隔符 'src' + '/' + 'index.js' path.join('src', 'index.js')
用 path.sep 引用分隔符常量 str.split('/') str.split(path.sep)
用 path.resolve 获取绝对路径 process.cwd() + '/' + file path.resolve(file)
用 fileURLToPath 转换 file URL import.meta.url.slice(7) fileURLToPath(import.meta.url)
避免 __dirname 在 ESM 不存在 直接使用 __dirname 用 import.meta.url 替代

▶ 示例:(2) 常见错误模式

JAVASCRIPT
const path = require('path');

// ❌ 硬编码分隔符
const bad1 = '/app/data/' + 'config.json';

// ✅ 使用 path.join
const good1 = path.join('/app', 'data', 'config.json');

// ❌ 手动拼接工作目录
const bad2 = process.cwd() + '/output/result.log';

// ✅ 使用 path.resolve
const good2 = path.resolve('output', 'result.log');

// ❌ 字符串替换分隔符
const bad3 = somePath.replace(/\\/g, '/');

// ✅ 使用 path.normalize
const good3 = path.normalize(somePath);
▶ 试一试

8. 8 综合示例:跨平台文件路径工具

以下示例模拟 Bob 修复后的 CLI 工具核心逻辑:读取配置路径、拼接数据目录、解析 URL、提取参数。

JAVASCRIPT
const path = require('path');
const { fileURLToPath } = require('url');

class PathTool {
  constructor(baseDir) {
    this.baseDir = baseDir || process.cwd();
  }

  resolveConfigPath(configRelativePath) {
    return path.resolve(this.baseDir, configRelativePath);
  }

  buildDataPath(...segments) {
    return path.join(this.baseDir, 'data', ...segments);
  }

  parseUrl(urlString) {
    const myUrl = new URL(urlString);
    return {
      protocol: myUrl.protocol,
      hostname: myUrl.hostname,
      pathname: myUrl.pathname,
      params: Object.fromEntries(myUrl.searchParams.entries())
    };
  }

  extractFileInfo(filePath) {
    const parsed = path.parse(filePath);
    return {
      directory: parsed.dir,
      fileName: parsed.name,
      extension: parsed.ext,
      fullPath: filePath
    };
  }

  toFilePath(urlOrPath) {
    if (urlOrPath.startsWith('file://')) {
      return fileURLToPath(urlOrPath);
    }
    return path.resolve(urlOrPath);
  }
}

const tool = new PathTool('/app/project');

// 1. 解析配置路径
const configPath = tool.resolveConfigPath('config/app.json');
console.log('配置路径:', configPath);
// /app/project/config/app.json

// 2. 拼接数据目录
const dataPath = tool.buildDataPath('users', 'profiles.json');
console.log('数据路径:', dataPath);
// /app/project/data/users/profiles.json

// 3. 解析 URL 并提取参数
const parsed = tool.parseUrl('https://api.example.com/v1/users?role=admin&active=true');
console.log('URL 解析:', parsed);
// { protocol: 'https:', hostname: 'api.example.com',
//   pathname: '/v1/users', params: { role: 'admin', active: 'true' } }

// 4. 提取文件信息
const info = tool.extractFileInfo('/app/project/data/users/profiles.json');
console.log('文件信息:', info);
// { directory: '/app/project/data/users',
//   fileName: 'profiles', extension: '.json', fullPath: '...' }

// 5. file URL 转文件路径
const localPath = tool.toFilePath('file:///app/project/config/app.json');
console.log('本地路径:', localPath);
// /app/project/config/app.json

❓ 常见问题

Q path.join 和 path.resolve 有什么区别?
A path.join 只拼接路径片段并规范化,不保证结果为绝对路径;path.resolve 从右向左解析,直到产生绝对路径,若未遇到绝对路径则以 process.cwd() 为基准。
Q 为什么要用 path.join 而不是字符串拼接?
A path.join 自动使用当前系统的路径分隔符,处理 ... 规范化,避免硬编码 /\ 导致的跨平台兼容问题。
Q __dirname 在 ESM 中能用吗?
A 不能。ESM 模块中没有 __dirname 和 __filename 全局变量,需要通过 path.dirname(fileURLToPath(import.meta.url)) 来获取等效值。
Q url.parse 被废弃了吗?
A 是的,url.parse 已被标记为废弃(deprecated),推荐使用 WHATWG 标准的 new URL() 构造函数,它提供更好的错误处理和内置 searchParams 支持。
Q 如何在 Windows 和 macOS/Linux 之间兼容路径?
A 始终使用 path.join / path.resolve 拼接路径,使用 path.sep 引用分隔符,使用 path.delimiter 处理环境变量,避免任何硬编码的分隔符字符串。
Q import.meta.url 返回的是什么?
A 返回当前模块的 file:// 协议 URL 字符串(如 file:///app/src/index.js),需要用 fileURLToPath() 转换为文件系统路径。
Q path.isAbsolute 在不同平台行为一致吗?
A 不一致。在 POSIX 上 /usr/local 是绝对路径,在 Windows 上 C:\Users 才是绝对路径,/usr/local 不是。path.isAbsolute 根据当前平台判断。

📖 小节


📝 作业

  1. 完成本课所有代码示例,确保每个示例都能正确运行
  2. 修改综合示例,添加自己的扩展功能
  3. 查阅官方文档,找出本课未涉及的1-2个API并编写测试代码
  4. 思考:在实际项目中,你会如何应用本课学到的知识?
  5. 尝试将本课知识与前面课程的内容结合,构建一个小项目
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏