掌握Node.js的路径模块

324 阅读5分钟

Node.js path模块是一个内置的模块,可以帮助你以独立于操作系统的方式处理文件系统的路径。如果你正在构建一个支持OSX、Linux和Windows的CLI工具,那么路径模块就必不可少。

即使你正在构建一个只在Linux上运行的后端服务,路径模块仍然有助于避免操作路径时的边缘情况。

在这篇博文中,我将描述一些使用路径模块的常见模式,以及为什么你应该使用路径模块而不是将路径操作成字符串。

在Node中连接路径模块

路径模块中最常用的函数是path.join()path.join() 函数将一个或多个路径段合并成一个字符串,如下图所示。

const path = require('path');

path.join('/path', 'to', 'test.txt'); // '/path/to/test.txt'

你可能想知道为什么要用path.join() ,而不是用字符串连接法

'/path' + '/' + 'to' + '/' + 'test.txt'; // '/path/to/test.txt'

['/path', 'to', 'test.txt'].join('/'); // '/path/to/test.txt'

有两个主要原因。

首先,为了支持Windows。Windows使用反斜线 (\) 而不是正斜线 (/) 作为路径分隔符。path.join() 函数为你处理这个问题,因为path.join('data', 'test.txt') 在Linux和OSX上都返回'data/test.txt' ,而在Windows上返回'data\\test.txt'

第二,用于处理边缘情况。在处理文件系统路径时,会出现许多边缘情况。例如,如果你试图手动连接两个路径,你可能会意外地得到一个重复的路径分隔符。path.join() 函数为你处理前面和后面的斜线,像这样。

path.join('data', 'test.txt'); // 'data/test.txt'
path.join('data', '/test.txt'); // 'data/test.txt'
path.join('data/', 'test.txt'); // 'data/test.txt'
path.join('data/', '/test.txt'); // 'data/test.txt'

解析Node中的路径

路径模块也有几个函数用于提取路径组件,如文件扩展名或目录。例如,path.extname() 函数将文件扩展名作为一个字符串返回。

path.extname('/path/to/test.txt'); // '.txt'

就像连接两个路径一样,获取文件扩展名比它最初看起来要棘手。如果有一个目录的名称中含有. ,或者如果路径是一个点状文件,那么在字符串中最后一个. 后的所有内容都不起作用。

path.extname('/path/to/github.com/README'); // ''

path.extname('/path/to/.gitignore'); // ''

path模块也有path.basename()path.dirname() 函数,它们分别获取文件名(包括扩展名)和目录。

path.basename('/path/to/test.txt'); // 'test.txt'

path.dirname('/path/to/test.txt'); // '/path/to'

你是否同时需要扩展名和目录?path.parse() 函数返回一个包含路径的对象,该路径被分解成五个不同的部分,包括扩展名和目录。path.parse() 函数也是你获得没有任何扩展名的文件名的方法。

/*
{
  root: '/',
  dir: '/path/to',
  base: 'test.txt',
  ext: '.txt',
  name: 'test'
}
*/
path.parse('/path/to/test.txt');

使用path.relative()

path.join()path.extname() 等函数涵盖了处理文件路径的大多数使用情况。但路径模块还有几个更高级的函数,如path.relative()

path.relative() 函数接收两个路径并返回第二个路径相对于第一个路径的路径。

// '../../layout/index.html'
path.relative('/app/views/home.html', '/app/layout/index.html');

当你得到相对于一个目录的路径,但想要相对于另一个目录的路径时,path.relative() 函数就很有用。例如,流行的文件系统监视库Chokidar给你相对于被监视目录的路径。

const watcher = chokidar.watch('mydir');

// if user adds 'mydir/path/to/test.txt', this
// prints 'mydir/path/to/test.txt'
watcher.on('add', path => console.log(path));

这就是为什么大量使用Chokidar的工具,例如Gatsby或webpack,往往也在内部大量使用path.relative()

例如,这里是Gatsby使用path.relative() 函数来帮助同步一个静态文件目录

export const syncStaticDir = (): void => {
  const staticDir = nodePath.join(process.cwd(), `static`)
  chokidar
    .watch(staticDir)
    .on(`add`, path => {
      const relativePath = nodePath.relative(staticDir, path)
      fs.copy(path, `${process.cwd()}/public/${relativePath}`)
    })
    .on(`change`, path => {
      const relativePath = nodePath.relative(staticDir, path)
      fs.copy(path, `${process.cwd()}/public/${relativePath}`)
    })
}

现在,假设一个用户在static 目录中添加了一个新文件main.js 。卓奇达调用on('add') 事件处理程序,将path 设置为static/main.js 。然而,当你把文件复制到/public ,你不希望有额外的static/

调用path.relative('static', 'static/main.js') 会返回相对于staticstatic/main.js 的路径,这正是你想要的,如果你想把static 的内容复制到public

跨操作系统的路径和URL

默认情况下,路径模块会根据你的Node进程所运行的操作系统在POSIX(OSX,Linux)和Windows模式之间自动切换。

然而,路径模块确实有办法在POSIX上使用Windows的路径模块,反之亦然。path.posixpath.win32 属性分别包含路径模块的 POSIX 和 Windows 版本。

// Returns 'path\\to\\test.txt', regardless of OS
path.win32.join('path', 'to', 'test.txt');

// Returns 'path/to/test.txt', regardless of OS
path.posix.join('path', 'to', 'test.txt');

在大多数情况下,根据检测到的操作系统自动切换路径模块是正确的行为。但是使用path.posixpath.win32 属性对于测试或总是想输出 Windows 或 Linux 风格的路径的应用程序是有帮助的。

例如,一些应用程序使用path.join()path.extname() 等函数来处理URL路径。

// 'https://api.mydomain.app/api/v2/me'
'https://api.mydomain.app/' + path.join('api', 'v2', 'me');

这种方法在Linux和OSX上可行,但如果有人试图在Azure Functions上部署你的应用,会发生什么?

你会发现'https://api.mydomain.app/api\\v2\\me' ,这不是一个有效的URL!如果你使用路径模块来处理URL,你应该使用path.posix

总结

Node path模块是处理文件系统路径的一个很好的工具,特别是在连接和解析方面。虽然你可以把文件路径当作字符串来操作,但在处理路径时有许多微妙的边缘情况。

一般来说,你应该使用路径模块来获取文件扩展名和连接路径,因为如果你把路径当作字符串来操作,很容易犯错误。

The postMastering the Node.js path moduleappeared first onLogRocket Blog.