前言
如果想用 node
写一些涉及文件处理的程序时,经常会碰到文件读写的步骤,而文件读写操作首先先需要确定路径, node 中则提供了很多变量和方法帮助我们完成这一工作,例如 __filename
、process.pwd
等。
为了让我们写程序时,快速灵活使用各种方式拼接路径,本文将相关方法辨析如下。
获取文件路径的方法及辨析
- __filename
返回当前【执行文件】所在的绝对路径。
值得注意的是,__filename
返回路径是当前【执行文件】所在的路径,例如有: blog/dir.js
和 blog/sub-dir/sub-dir.js
文件,其代码为:
// blog/dir.js 文件
require('./sub-dir/sub-dir')
console.log('__filename :>> ', __filename);
// blog/sub-dir/sub-dir.js 文件
console.log('__filename :>> ', __filename);
在 C:\Users\isaaczhou\Desktop\博客项目\dir-test\
执行 node .\blog\dir.js
,结果为:
__filename :>> C:\Users\isaaczhou\Desktop\博客项目\dir-test\blog\sub-dir\sub-dir.js
__filename :>> C:\Users\isaaczhou\Desktop\博客项目\dir-test\blog\dir.js
__filename
是由当前正在执行的模块文件所确定,我们可以通过另外一个例子来确定:
require('./sub-dir/sub-dir')
console.log('__filename :>> ', __filename);
// blog/sub-dir/sub-dir.js 文件
function printFileName() {
console.log('__filename :>> ', __filename);
}
可见,不管如何被其他文件调用,__filename
返回的值都是执行文件所在的目录。
- __dirname
返回当前【执行文件】所在目录的绝对路径,参考官网,它的效果类似 path.dirname(__filename)
。
// blob/sub-dir/dir.js
const path = require('path');
console.log(__dirname);
// 打印: C:\Users\isaaczhou\Desktop\博客项目\dir-test\blog\sub-dir
console.log(path.dirname(__filename));
// 打印: C:\Users\isaaczhou\Desktop\博客项目\dir-test\blog\sub-dir
那么其实它与 __dirname
区别,就是是否包含当前文件的路径,其他方面的特征应该完全相同。
- process.cwd()
process 即为当前 node 的 node 进程, process.cwd()
方法则是获取该进程的工作目录,也就是触发的 node 命令的路径。
# 在 C:\Users\isaaczhou\Desktop\博客项目\dir-test 执行 node ./blog/cwd.js
$ node ./blog/cwd.js
process.cwd() :>> C:\Users\isaaczhou\Desktop\博客项目\dir-test
那么和上文的 __filename
区别就是:process.cwd()
的返回值在 node 进程一启动时就决定了,不会像 __filename
那样执行不同文件就是不同的。我们可以把 cwd 当作 node global 属性,而 __filename
和 __dirname
则是每个模块的局部属性,这点可以在 node require 的源码中看到:
// eslint-disable-next-line func-style
let wrap = function(script) {
return Module.wrapper[0] + script + Module.wrapper[1];
};
const wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});',
];
可以看出所有被 require 加载的模块内容都会被注入 __filename
__dirname
的变量。
- path.resolve('./') 和 path.resolve('/')
./
表示从工作区目录开始,所以返回的是当前工作目录的绝对路径。/
表示从系统的根目录开始,所以返回的是系统根目录的绝对路径。
- 与 require 的辨析
基于当前文件的相对路径寻找其他文件需要有一个前提,就是你有方法知道【当前文件】的信息,此时唯一的方法就是上文的 __dirname
(或者__filename
)。
这时会有疑问,require
都可以直接基于当前文件相对路径,寻找其他文件的位置时,为何我们我们还需要用借助 __dirname
拼接路径呢?
参考 StackOverflow 上一则回答:除了 require 的几乎所有的函数都会把【文件路径】当作 fs 的参数,而 fs 是 node 下通用模块,不会拿到当前文件信息,所以当想基于当前 js 文件使用相对路径去访问其他文件时,除了 require,都需要结合 __dirname
去拼接。
之所以 require 能够拿到当前文件信息,是因为 require 针对每个 js 文件都是唯一的(而不是像 fs 那么通用)。
可以将 require 看成为一个闭包返回的函数,并为里边注入了当前 js 文件的路径信息,每个文件下的 require 都是不一样的引用。
function createRequire() { return (relPath) => { const realpath = path.resolve(curPath, request); // 加载模块内容,并注入 __filename 和 __dirname // ...loadModule(realpath) } } const myRequire = createRequire(); myRequire('./xxx');
require 是从入口模块开始逐级寻找子模块的,所以当加载子模块时,已经拥有了 parent 模块的路径,require 初始是从 package.json
中 main
或者 process.cwd()
出发寻找,所以我们才能在每个子模块下拿到当前模块的路径信息,并进而传给 __dirname
__filename
,具体可以参考node/loader.js at main · nodejs/node (github.com)。
参考
www.geeksforgeeks.org/difference-…
node/loader.js at main · nodejs/node (github.com)
What's the difference between process.cwd() vs __dirname? (newbedev.com)