起因
原文收錄在我的 GitHub博客 (https://github.com/jawil/blog) ,喜歡的可以關(guān)注最新動態(tài)宵晚,大家一起多交流學習垂攘,共同進步,以學習者的身份寫博客淤刃,記錄點滴晒他。
最近在學習Node.js
里面的fs
模塊,遇到了一個比較詭異的現(xiàn)象逸贾,踩到了坑陨仅,就是讀取當前目錄下的一個文件津滞,死活讀取不到,由于之前對于Node.js
里面的path
模塊也不太熟悉灼伤,也沒系統(tǒng)研究過触徐,所以今天就踩了這個坑,記錄踩坑的過程狐赡,防止以后踩坑和大家也踩坑撞鹉。
說一下當時的情形:
我納悶的很半天,我明明就是讀取當前目錄下的1.findLargest.js
颖侄,為什么提示找不到這個文件鸟雏,運行了幾遍,死活找不到1.findLargest.js
這個文件览祖。
后來才發(fā)現(xiàn)是因為運行這個文件不是從當前目錄運行了孝鹊,從圖中可以看出,當前的目錄是/Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs
穴墅,而我運行這個腳本的目錄是/Users/jawil/Desktop/nodejs/demo/ES6-lottery
惶室;這就是問題的所在了。不過為什么運行腳本的位置也會影響這個路徑呢玄货,且往下看皇钞。
探索
計算機不會欺騙人,一切按照規(guī)則執(zhí)行松捉,說找不到這個文件夹界,那肯定就是真的找不到,至于為什么找不到隘世,那就是因為我們理解有偏差可柿,我最初理解的'./'是當前執(zhí)行js
文件所在的文件夾的絕對路徑,然后Node.js
的理解卻不是這樣的丙者,我們慢慢往下看复斥。
Node.js
中的文件路徑大概有 __dirname
, __filename
, process.cwd()
, ./
或者 ../
,前三個都是絕對路徑械媒,為了便于比較目锭,./
和 ../
我們通過 path.resolve('./')
來轉(zhuǎn)換為絕對路徑。
簡單說一下這幾個路徑的意思纷捞,:
__dirname: 獲得當前執(zhí)行文件所在目錄的完整目錄名
__filename: 獲得當前執(zhí)行文件的帶有完整絕對路徑的文件名
process.cwd():獲得當前執(zhí)行node命令時候的文件夾目錄名
./: 文件所在目錄
先看一看我電腦當前的目錄結(jié)構(gòu):
syntax/
-nodejs/
-1.findLargest.js
-2.path.js
-3.fs.js
-regs
-regx.js
-test.txt
在 path.js 里面我們寫這些代碼痢虹,看看輸出是什么:
const path = require('path')
console.log('__dirname:', __dirname)
console.log('__filename:', __filename)
console.log('process.cwd():', process.cwd())
console.log('./:', path.resolve('./'))
在當前目錄下也就是nodejs
目錄運行 node path.js
,我們看看輸出結(jié)果:
__dirname: /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs
__filename: /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs/2.path.js
process.cwd(): /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs
./: /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs
然后在 項目根目錄ES6-lottery
目錄下運行 node syntax/nodejs/2.path.js
主儡,我們再來看看輸出結(jié)果:
__dirname: /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs
__filename: /Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs/2.path.js
process.cwd(): /Users/jawil/Desktop/nodejs/demo/ES6-lottery
./: /Users/jawil/Desktop/nodejs/demo/ES6-lottery
答案顯而易見奖唯?我們可以通過上面的例子對比,暫時得出表面的結(jié)論:
- __dirname: 總是返回被執(zhí)行的 js 所在文件夾的絕對路徑
- __filename: 總是返回被執(zhí)行的 js 的絕對路徑
- process.cwd(): 總是返回運行 node 命令時所在的文件夾的絕對路徑
- ./: 跟 process.cwd() 一樣糜值,返回 node 命令時所在的文件夾的絕對路徑
但是丰捷,我們再來看看這個例子坯墨,我們在上面的例子加幾句代碼,然后:
我們在1.findLargest.js
先加這句代碼
exports.A = 1;
再來在剛才報錯的3.fs.js
里面加這兩句代碼看看:
const test = require('./1.findLargest.js');
console.log(test)
運行node syntax/nodejs/3.fs.js
瓢阴,最后看看結(jié)果:
再次疑惑
為什么都是讀取./1.findLargest.js
文件畅蹂,一樣的路徑,為什么require
能獲取到荣恐,而readFile
讀取不到呢?
于是查了不少資料累贤,看到了一些關(guān)于require
引入模塊的機制叠穆,從中學到了不少,也明白了為什么是這樣臼膏。
我們先了解一下require()
的基本用法:
下面的內(nèi)容來自require() 源碼解讀,由阮一峰翻譯自《Node使用手冊》硼被。
我們從第(2)小條的a部分可以看出:
(2)如果 X 以 "./" 或者 "/" 或者 "../" 開頭
a. 根據(jù) X 所在的父模塊,確定 X 的絕對路徑渗磅。
b. 將 X 當成文件嚷硫,依次查找下面文件,只要其中有一個存在始鱼,就返回該文件仔掸,不再繼續(xù)執(zhí)行。
const test = require('./1.findLargest.js')
按照上面規(guī)則翻譯一遍就是:
根據(jù)
1.findLargest.js
所在的父模塊医清,確定1.findLargest.js
的絕對路徑為/Users/jawil/Desktop/nodejs/demo/ES6-lottery/syntax/nodejs
起暮,關(guān)于其中的尋找細節(jié)這里不做探討。先把
1.findLargest.js
當成文件会烙,依次查找當前目錄下的1.findLargest.js
负懦,找到了,就返回該文件柏腻,不再繼續(xù)執(zhí)行纸厉。
根據(jù)require
的基本規(guī)則,對于上面出現(xiàn)的情形也就不足為奇了五嫂,更多require
的機制和源碼解讀颗品,請移步:
require() 源碼解讀。
那么關(guān)于 ./
正確的結(jié)論是:
在 require()
中使用是跟 __dirname
的效果相同贫导,不會因為啟動腳本的目錄不一樣而改變抛猫,在其他情況下跟 process.cwd()
效果相同,是相對于啟動腳本所在目錄的路徑孩灯。
總結(jié):
- __dirname: 獲得當前執(zhí)行文件所在目錄的完整目錄名
- __filename: 獲得當前執(zhí)行文件的帶有完整絕對路徑的文件名
- process.cwd():獲得當前執(zhí)行node命令時候的文件夾目錄名
- ./: 不使用require時候闺金,./與process.cwd()一樣,使用require時候峰档,與__dirname一樣
只有在 require()
時才使用相對路徑(./, ../)
的寫法败匹,其他地方一律使用絕對路徑寨昙,如下:
// 當前目錄下
path.dirname(__filename) + '/path.js';
// 相鄰目錄下
path.resolve(__dirname, '../regx/regx.js');
最后看看改過之后的結(jié)果,不會報錯找不到文件了掀亩,不管在哪里執(zhí)行這個腳本文件舔哪,都不會出錯了,防止以后踩坑槽棍。