上次写删除文件夹的时候用到了fs
模块,也集中用到了很多种路径,当时就想写一下,在Node中使用各种路径的问题,于是就简单写了一下,可以从 这里 获取demo源代码。
刚写Node的时候经常会遇到这种情况:比如项目入口是app.js
,而app.js
并不一定在根目录下,比如他在bin
目录下,到了启动项目时,使用node /bin/app.js
和进入bin
文件夹直接node app.js
总会有一个起不起来,都是些路径找不到的错误,究其原因就是启动应用时执行的目录不同了,不过为什么启动脚本的位置会有这么多影响呢,让我们来总结一下。
对比常用的几种路径
Node 中的文件路径大概有 __dirname
,__filename
,process.cwd()
,./
或者 ../
。前三个都是绝对路径,为了便于比较,./
和 ../
我们通过 path.resolve('./')
来转换为绝对路径。先看一个简单的例子。
假如我们有这样的文件结构:
在 server.js
里编写如下的代码:
1 | var path = require('path'); |
在 path-test
目录下运行 node bin/server.js
得到的结果:
进入 bin
目录下运行 node server.js
得到的结果:
现在我们可以总结下这几个路径的意思:
1 | __dirname: Nodejs的一个全局变量,获得当前执行文件所在目录的完整目录名 |
注意__dirname
得到的目录和命令执行所在的目录、__filename
得到的文件名和参数指定的文件名都不一定相同,因为可能在一个文件中调用了另一个目录中的另一个文件。
更复杂的情况
我们把例子升级一下,在bin
目录下新建一个test.js
:
1 | var fs = require('fs'); |
现在目录结构如下:
我们这次先进入bin
目录执行node test.js
,得到的结果:
可以看到是正常的,然后我们退出bin
目录,在上一级执行node bin/test.js
,得到结果:
我们可以看到报错了,但是require
是OK的,只是fs.readFile
时路径出现了错误。从第一个例子我们可以知道,使用相对路径出现错误是预期之内的,因为在bin
目录外执行时目录下已经没有server.js
这个文件了,但是为什么在require
中使用相对路径,就不受启动应用时执行命令目录不同的影响呢?实际上是require
有自己的搜索机制,具体可以看require() 源码解读。
使用path模块处理文件路径
面对复杂的路径问题,path模块可以帮你规范化连接和解析路径,还可以用于绝对路径到对路径的转换、提取路径的组成部分及判断路径是否存在等。常用的两个命令:
path.join
path.join()方法可以连接任意多个路径字符串,只是简单的连接,不会看是否真的存在。
1 | var path = require('path'); |
path.resolve
path.resolve()方法可以将多个路径解析为一个规范化的绝对路径。其处理方式类似于对这些路径逐一进行cd操作,与cd操作不同的是,这引起路径可以是文件,并且可不必实际存在(resolve()方法不会利用底层的文件系统判断路径是否存在,而只是进行路径字符串操作)。
1 | path.resolve('foo/bar', '/tmp/file/', '..', 'a/../subfile') |
其处理方式类型于
1 | cd foo/bar |
如果解析的不是绝对路径,path.resolve()会将当前工作目录(非进程工作目录)加到解析结果的前面。例如:
1 | path.resolve('/foo/bar', './baz') |
两者区别
- join只是把各个path片段连接在一起, resolve会把‘/’当成根目录
1 | path.join('/a', '/b') // 返回 '/a/b' |
- join直接拼接字段,resolve解析路径,如果解析的不是绝对路径,会在前面增加当前文件所在的目录
1 | path.join("a", "b1", "..", "b2") // 返回 'a/b2' |
相同之处
- 都会规范化路径
- 都不会去验证路径是否真的存在
总结一下
最好只在 require()
时才使用相对路径./
或者../
的写法,其他地方一律配合
path`模块使用绝对路径。