
前言
相信大家一定用過(guò) path.resolve() 或 path.join(),特別是 Webpack、Rollup、Vite 等構(gòu)建工具,再熟悉不過(guò)了。
path.resolve(__dirname, 'src/index.js')
像這個(gè)例子,用 path.join(__dirname, 'src/index.js') 所得到的結(jié)果也是完全一樣的。
那么它倆究竟有何不同,在什么情況下使用才能體現(xiàn)出區(qū)別呢?
path.resolve
該函數(shù)接受「零或多個(gè)」字符串類型的路徑參數(shù),并返回一個(gè) normalized 的「絕對(duì)路徑」。
path.resolve(path1, path2, ..., pathN)
有以下幾個(gè)特點(diǎn):
- 其中 zero-length 的參數(shù)會(huì)被忽略,比如
''(空字符串)和'.'(表示當(dāng)前目錄)。- 以
/開(kāi)頭的參數(shù)會(huì)被當(dāng)作文件系統(tǒng)根路徑,不以../或./開(kāi)頭的參數(shù)會(huì)被當(dāng)作是目錄。- 若參數(shù)不為字符串類型,則會(huì)拋出 TypeError。
- 若不傳遞任何參數(shù)時(shí),返回 Node 進(jìn)程的當(dāng)前工作目錄。因此
path.resolve() === process.cwd()結(jié)果為true。- 結(jié)果返回之前其內(nèi)部也會(huì)做類似
path.normalize()的路徑規(guī)范化處理。
看例子:
path.resolve('/a', 'b', 'c') // return: "/a/b/c"
path.resolve('/a', '/b', 'c') // return: "/b/c"
path.resolve('/a', '/b', '/c') // return: "/c"
其實(shí)在官網(wǎng)就描述得很清晰了。無(wú)非就是,「從右到左」一個(gè)一個(gè)參數(shù)開(kāi)始解析,每解析一個(gè)就將其加到原來(lái)路徑的「前面」,參數(shù)之間使用平臺(tái)對(duì)應(yīng)的路徑分隔符連接。若能拼接成一個(gè)絕對(duì)路徑,就停止解析并立即返回。如果將所有參數(shù)都解析完,仍然無(wú)法拼湊得出一個(gè)絕對(duì)路徑,那么就講這些參數(shù)的結(jié)果拼到當(dāng)前工作目錄中,以得出絕對(duì)路徑。
因此,上述示例內(nèi)部解析過(guò)程如下:
"c" -> "b/c" -> "/a/b/c"
"c" -> "/b/c"
"/c"
假設(shè)有 path.resolve('a', 'b'),就是 "b" -> "a/b" -> "process.cwd()/a/b" 的過(guò)程,相當(dāng)于 path.resolve(process.cwd(), 'a', 'b')。其中 proccess.cwd() 就是當(dāng)前工作目錄。
個(gè)人認(rèn)為,更好的理解倒是「從左往右」看,將 path.resolve() 方法看作 Shell 的 cd 操作,只是前者不管文件系統(tǒng)是否存在此目錄或文件。如偽代碼:
path.resolve('/a', '/b', 'c')
// 相當(dāng)于
$ cd /a; cd /b; cd c
或許
path.resolve()稱為path.cd()更讓人豁然開(kāi)朗吧。
path.join
該函數(shù)接受「零或多個(gè)」字符串類型的路徑參數(shù),返回一個(gè)所有參數(shù)拼接起來(lái)的(相對(duì)/絕對(duì))路徑。
path.join(path1, path2, ..., pathN)
有以下幾個(gè)特點(diǎn):
- 其中 zero-length 的參數(shù)會(huì)被忽略,比如
''(空字符串)和'.'(表示當(dāng)前目錄)。- 以
../開(kāi)頭的參數(shù)認(rèn)為是上一級(jí)目錄。- 第一個(gè)參數(shù)若以
/會(huì)被認(rèn)為是根目錄,其他以/開(kāi)頭的參數(shù)作用與./相同。- 若參數(shù)不為字符串類型,則會(huì)拋出 TypeError。
- 若不傳遞任何參數(shù)時(shí),返回
.(當(dāng)前目錄)。- 結(jié)果返回之前其內(nèi)部也會(huì)做類似
path.normalize()的路徑規(guī)范化處理。
看例子:
path.join('a', 'b', 'c') // return: "a/b/c"
path.join('/a', 'b', 'c') // return: "/a/b/c"
path.join('/a', '/b', 'c') // return: "/a/b/c"
path.join('/a', '/b', '/c') // return: "/a/b/c"
其實(shí)不用看那么多,換個(gè)角度去理解或許更清晰,兩個(gè)步驟:
- 用相加運(yùn)算符
+將所有參數(shù)連接起來(lái)(參數(shù)之間用/連接)。- 使用
path.normalize()對(duì)相加后的字符串路徑作規(guī)范化處理。
有人可能會(huì)問(wèn),path.normalize() 又是干嘛的啊。很簡(jiǎn)單:把 ./、../ 翻譯成對(duì)應(yīng)路徑;把多余無(wú)用的路徑連接符干掉(如 a//b => a/b);將路徑連接符轉(zhuǎn)換為特定平臺(tái)的連接符(比如 Unix 的 /,Windows 的 \)。
因此,可以把 path.join('/a', '/b', 'c') 理解成這樣:
let args = ['/a', '/b', 'c']
let str = args.join('/') // "/a//b/c"
str = path.normalize(str) // "/a/b/c"
區(qū)別
以幾個(gè)示例做總結(jié):
無(wú)參數(shù)時(shí)
path.resolve() // 返回當(dāng)前工作目錄,相當(dāng)于 `process.cwd()`,是絕對(duì)路徑。
path.join() // 返回 `.`(當(dāng)前目錄),是相對(duì)路徑。
?? 請(qǐng)注意「當(dāng)前工作目錄」和「當(dāng)前目錄」的區(qū)別。
有多個(gè)參數(shù),且中間參數(shù)以 / 開(kāi)頭
path.resolve('/a', '/b', 'c') // 返回 `/b/c`,絕對(duì)路徑。
path.join('/a', '/b', 'c') // 返回 `/a/b/c`,絕對(duì)路徑。
path.resolve('a', '/b', 'c') // 返回 `/b/c`,絕對(duì)路徑。
path.join('a', '/b', 'c') // 返回 `a/b/c`,相對(duì)路徑。
相信大家都懂了。
The end.