Skip to content
0

node:path

超级常用的一个模块,例如在开发 nodejs 命令行工具的时候, 处理路径的时候,需要用到这个模块

口径

路径的不同部分有不同的叫法,首先需要知道各部分叫什么,便于我们在代码中命名

对于路径 /a/b/c/d/index.html,我们称:

  • dirname => /a/b/c/d
  • basename => index.html
  • extname => .html

如表所示:

/home/user/dir/file.txt
rootnameext
dirbase

join/resolve

两个方法都用于拼接路径,但是略有不同

  1. join 方法在于抹除 / 的影响,例如下面的例子,不关心传入的路径中,前后是否有 /,拼接的时候,会自动抹去和添加 /,使之成为合法的路径
import { join } from 'node:path'

console.log(
    join('/a/', 'b', '/c'), // => /a/b/c
    join('/a/', '/b', '/c') // => /a/b/c
    join('/a', '/b', '/c') // => /a/b/c
    join('a', '/b', '/c') // => a/b/c
)
  1. resolve 会从当前目录(cwd)开始计算,而且默认情况下是返回绝对路径
import { resolve } from 'node:path'

console.log(
    resolve('a', 'b', 'c'),
    // => /Users/peterroe/project/a/b/c
    resolve('./a', 'b', 'c')
    // => /Users/peterroe/project/a/b/c
)

而且更像是在执行 shellcd 命令,这意味着参数中如果存在像 /a 这样的绝对路径,则会返回到根目录:

resolve('a', 'b/c'),     //=> /Users/peterroe/project/a/b/c
resolve('a', '/b/c')     //=> /b/c
resolve('a', 'b','../c') //=> a/c

basename

返回路径最后的名称,第二个参数可以传入要删除的可选后缀

import { basename } from 'node:path'

basename('/a/b/c/d/demo'),               //=> demo
basename('/a/b/c/d/demo/'),              //=> demo
basename('/a/b/c/d/demo.html'),          //=> demo.html
basename('/a/b/c/d/demo.html', '.html'), //=> demo
basename('/a/b/c/d/demo.html', '.css'),  //=> demo.html

dirname

返回路径的目录名

import { dirname } from 'node:path'

dirname('/a/b/c/d/demo'),        //=> /a/b/c/d
dirname('/a/b/c/d/demo/'),       //=> /a/b/c/d
dirname('/a/b/c/d/demo.html'),   //=> /a/b/c/d

extname

返回路径或者文件的后缀名,只取最后一个

import { extname } from 'node:path'

extname('/a/b/c/d/demo'),        //=> ''
extname('/a/b/c/d/demo/'),       //=> ''
extname('/a/b/c/d/demo.html'),   //=> '.html'
extname('/a/b/c/d/demo.map.js'), //=> '.js'
extname('index.html'),           //=> '.html'
extname('.d.ts'),                //=> '.ts'

是否是绝对路径

import { isAbsolute } from 'node:path'

isAbsolute('/foo/bar'), // true
isAbsolute('/baz/..'),  // true
isAbsolute('qux/'),     // false
isAbsolute('.'),        // false

normalize

上面我们提到 resolve 方法可以像执行 cd 命令一样生成我们的路径,但是遗憾的是 resolve 的返回值是基于当前文件系统的绝对路径的,有时间不想要这种和本地文件系统有关的结果

这个时候可以使用 normalize,但是只能传入一个参数

import { resolve, normalize, join } from 'node:path'
const pathOne = 'a'
const pathTwo = 'b'
const pathThree = '../c'

resolve(pathOne, pathTwo, pathThree), //=> /path/to/project/a/c
// 即保存绝对路径,又和本地文件系统无关
normalize([pathOne, pathTwo, pathThree].join('/')), //=> /a/c  
join(pathOne, pathTwo, pathThree), //=> a/c

// 当然,更多的情况是处理本身就是一个错乱的路径
normalize('/foo/bar//baz/asdf/quux/..') //=> /foo/bar/baz/asdf

parse

解析文件路径

parse('/home/user/dir/file.txt')
/*
{
    root: '/',
    dir: '/home/user/dir',
    base: 'file.txt',
    ext: '.txt',
    name: 'file'
}
*/

format

parse 通常就会有 format,可以传入几个参数,有不同的优先级,详细点击这里

relative

如果说 resolve(A, B) = C,那么 relative 就像 relative(A, C) = B

relative(A, C) = B 会计算从 AC 路径应该执行怎样的路径 B

resolve('/data/orandea/test/aaa', '../../impl/bbb'),
//=> /data/orandea/impl/bbb
relative('/data/orandea/test/aaa', '/data/orandea/impl/bbb')
//=> ../../impl/bbb

为什么需要 path?

我们使用 path 主要原因是抹去平台差距。

POSIXWindows 的路径是有差距的,使用 path 模块提供的方法可以帮我们抹去差距。

譬如进行路径分割的时候,无需关心到底使用 \ 还是 /,其他 path 导出的方法也类似。

// in POSIX
'foo/bar/baz'.split(path.sep);
// Returns: ['foo', 'bar', 'baz'] 

// in Windows
'foo\\bar\\baz'.split(path.sep);
// Returns: ['foo', 'bar', 'baz']

Windows vs. POSIX

nodejs 还提供了互相调用了一个平台的能力,互相可以调用对方平台的路径解析算法

通常来说,在 POSIX 上的程序处理的路径都是 POSIX 的路径, 在 Windows 上的程序处理的路径都是 Windows 的路径,一般不会有问题

但是如果在 POSIX 上要处理 Windows 上的路径(交换同理),就会有问题,比如:

import path from 'path'
// 在 POSIX 的机器上,处理 Windows 的路径,我们会得到不符合预期的答案
path.basename('C:\\temp\\myfile.html');
//=> C:\\temp\\myfile.html

// 应该调用 win 平台的算法
path.win32.basename('C:\\temp\\myfile.html')
//=> myfile.html

TIP

上面的例子不代表你需要写两套代码,当用户通过 npm 安装你的工具的时候,平台还是根据用户的操作系统使用正确的算法,只有尝试解析和本平台不同风格的路径才会出现问题

Released under the MIT License.