“幽靈殺手” pnpm是怎么做到“又快又省又穩(wěn)”(扒一扒pnpm實(shí)現(xiàn)原理)
Hi! 這里是剛吃完鮮蝦魚(yú)板面的JustHappy,上一次我們聊了聊主流的npm軟件包下載器,其中pnpm似乎是目前各方面最屌的一個(gè),所以這次我們來(lái)深扒一下pnpm的實(shí)現(xiàn)原理,也就是pnpm是如何“殺死幽靈”以及“又快又省又穩(wěn)的”,本文將會(huì)結(jié)合pnpm的官方文檔進(jìn)行解析
圖片
可能pnpm的特性比較多,小弟能力暫時(shí)有限,本篇文章只會(huì)涉及部分pnpm特性,如果大家有興趣,可以去官方文檔查看pnpm中文文檔 | pnpm中文網(wǎng)[1]
首先你得先搞明白“硬鏈接”和“軟鏈接”
是吧!死去的回憶又回來(lái)了,這倆是否讓你想起了逃過(guò)的計(jì)算機(jī)操作系統(tǒng)?沒(méi)關(guān)系,我們?cè)賮?lái)回顧一下
大白話回顧基礎(chǔ)概念
“硬鏈接(Hard Link)”
硬鏈接是咱文件系統(tǒng)中的一個(gè)數(shù)據(jù)實(shí)體,它是直接指向咱硬盤(pán)上的數(shù)據(jù)塊的,也就是說(shuō)硬鏈接就是目標(biāo)文件的另一個(gè)名字,如果目標(biāo)文件被刪除,硬鏈接依然有效,這怎么說(shuō)呢?因?yàn)樵蹐?zhí)行刪除目標(biāo)文件這一操作只是刪除了該目標(biāo)文件在文件系統(tǒng)中的一個(gè)名字,只要目標(biāo)文件的數(shù)據(jù)還有一個(gè)硬鏈接,那么這個(gè)數(shù)據(jù)就不會(huì)被真正的刪除
“軟鏈接 (Symbolic Link)”
軟鏈接,又叫做符號(hào)鏈接,這個(gè)比硬鏈接要好理解些,windows的快捷方式大家都使用過(guò)吧,軟鏈接就是一個(gè)指向文件或者目錄(文件路徑)的快捷方式,你刪除快捷方式的時(shí)候原文件不會(huì)受影響
以下是我將一些特性匯總了一個(gè)表格
特性 | 硬鏈接 (Hard Link) | 軟鏈接 (Symbolic Link) |
定義 | 一個(gè)指向文件inode的引用 | 一個(gè)指向文件路徑的引用 |
目標(biāo) | 必須指向文件,不能指向目錄 | 可以指向文件或目錄 |
刪除原文件 | 刪除原文件不會(huì)連接硬鏈接 | 刪除原文連接軟鏈接變?yōu)椤八梨溄印?/p> |
權(quán)限 | 硬鏈接和原文件共享相同的權(quán)限 | 軟鏈接有自己的權(quán)限設(shè)置 |
空間占用 | 不占用額外空間(除了目錄項(xiàng)) | 占用少量空間來(lái)存儲(chǔ)鏈接目標(biāo)路徑 |
移動(dòng)/重命名 | 移動(dòng)或重命名原文件會(huì)影響硬鏈接 | 移動(dòng)或重命名原文件不會(huì)影響軟鏈接,但軟鏈接會(huì)指向錯(cuò)誤路徑 |
我們先來(lái)看看使用pnpm生成的node_modules是什么樣的
圖片
如果你仔細(xì)比對(duì),會(huì)發(fā)現(xiàn)node_modules的一級(jí)目錄下所出現(xiàn)的包都是在項(xiàng)目的package.json中已經(jīng)被聲明的
我們還看到相比于使用npm構(gòu)建的node_module,這里多了個(gè) .pnpm文件夾,這其實(shí)是node_modules中存放包硬鏈接的地方
.pnpm 目錄下的文件結(jié)構(gòu)可以參照下圖
圖片
注:點(diǎn)我去官方文檔[2]
我們來(lái)對(duì)比npm畫(huà)幾張圖吧
我們來(lái)回顧一下npm的node_module結(jié)構(gòu)吧:
圖片
可以看到這是一個(gè)扁平化的結(jié)構(gòu),這直接導(dǎo)致了幽靈依賴的出現(xiàn)
那么結(jié)合上面對(duì)pnpm的分析,我們可以初步得到以下結(jié)構(gòu)(本人自己畫(huà)的,如果難看的話望見(jiàn)諒哈):
圖片
好吧,可能有些復(fù)雜,但是不慌,接下來(lái)我們結(jié)合這個(gè)圖講pnpm的三層尋址
pnpm的三層尋址
pnpm的三層尋址策略是其高效存儲(chǔ)和依賴管理的核心機(jī)制,具體包括以下三個(gè)層面:
第一層:全局的 pnpm 存儲(chǔ)
- 目的:允許跨項(xiàng)目共享依賴,進(jìn)一步減少存儲(chǔ)和下載的冗余。
- 原理:pnpm維護(hù)了一個(gè)全局的存儲(chǔ)(通常位于用戶的home目錄下),在其中保存了所有下載的包的版本。這些版本被硬鏈接到項(xiàng)目的.pnpm目錄。
這有什么好處呢?
這其實(shí)實(shí)現(xiàn)了一個(gè) 跨項(xiàng)目共享依賴 的目的,這意味著,不同的項(xiàng)目可以共享全局存儲(chǔ)中的同一個(gè)物理文件,從而不需要重復(fù)下載或存儲(chǔ)相同的文件。(極大了節(jié)省了下載依賴的時(shí)間)
第二層:項(xiàng)目級(jí)的 .pnpm 目錄
- 目的:.pnpm文件夾內(nèi)使用的是硬鏈接。這意味著每個(gè)包的文件都是硬鏈接到全局存儲(chǔ)中的文件。這些硬鏈接為單個(gè)項(xiàng)目提供一個(gè)集中的地方來(lái)存儲(chǔ)其所有依賴的軟鏈接(或符號(hào)鏈接),以減少重復(fù)并確保穩(wěn)定的包結(jié)構(gòu)。
- 原理:每個(gè)項(xiàng)目中的.pnpm目錄鏈接到全局pnpm存儲(chǔ)中的依賴版本。項(xiàng)目的node_modules目錄中的每個(gè)依賴實(shí)際上都是指向這個(gè).pnpm目錄中的相應(yīng)版本的軟鏈接。
.pnpm的作用
這確保了項(xiàng)目?jī)?nèi)的node_modules可以維持一個(gè)干凈和結(jié)構(gòu)化的布局,而真正的包文件都存儲(chǔ)在全局存儲(chǔ)中,并通過(guò)項(xiàng)目級(jí)的.pnpm目錄鏈接。
第三層:本地 node_modules
- 目的:維持項(xiàng)目結(jié)構(gòu)的語(yǔ)義性,提供一個(gè)確定性的依賴解析方式。
- 原理:每個(gè)項(xiàng)目的node_modules目錄中的直接依賴都被組織成與package.json中聲明的結(jié)構(gòu)相匹配的方式。這遵循了Node.js的模塊解析邏輯,確保每個(gè)依賴都能被正確地找到。
我們得到了一個(gè)更加接近Node.js模塊解析邏輯的目錄結(jié)構(gòu),并解決了“幽靈依賴”
幽靈依賴(Phantom Dependency)是指在項(xiàng)目中使用了某些依賴包,但這些包沒(méi)有顯式地聲明在項(xiàng)目的依賴列表(如 package.json 文件)中。這種依賴的存在通常是因?yàn)樗黄渌蕾嚨囊蕾嚕撮g接依賴)提供,而不是項(xiàng)目直接安裝的。
到此,我們得到了一個(gè)接近Node.js模塊解析邏輯的 “非扁平的” node_modules結(jié)構(gòu)
可能有些混亂,我們?cè)賮?lái)總結(jié)一遍
總結(jié)!為什么說(shuō)!又快!又省!又穩(wěn)!
有關(guān)于“快”
- pnpm擁有一個(gè)全局的存儲(chǔ)空間,所有的npm包都存儲(chǔ)在這個(gè)位置。這意味著,無(wú)論我們有多少個(gè)項(xiàng)目,對(duì)于同一個(gè)npm包,我們只需要下載一次并存儲(chǔ)一次在全局pnpm存儲(chǔ)中。這樣,當(dāng)我們?cè)诓煌?xiàng)目中需要同一個(gè)包時(shí),pnpm可以直接從全局存儲(chǔ)中鏈接,而不需要重復(fù)下載。
- 在每個(gè)項(xiàng)目的node_modules目錄中,pnpm使用軟鏈接(符號(hào)鏈接)或硬鏈接來(lái)引用全局存儲(chǔ)中的包。這些鏈接指向全局存儲(chǔ)中的實(shí)際文件,因此,無(wú)論在哪個(gè)項(xiàng)目中,我們都能快速訪問(wèn)到這些包,而不需要等待重復(fù)的下載過(guò)程。
- 由于pnpm避免了重復(fù)下載相同的包,它大大減少了網(wǎng)絡(luò)請(qǐng)求和磁盤(pán)I/O操作,這在網(wǎng)絡(luò)速度較慢或磁盤(pán)I/O性能有限的環(huán)境中尤其有用。
- 在安裝新項(xiàng)目依賴時(shí),pnpm首先檢查全局存儲(chǔ)中是否已經(jīng)存在所需的包。如果存在,它將直接鏈接到項(xiàng)目中,而不是重新下載,這大大加快了安裝速度。
- pnpm支持并行安裝,這意味著它可以同時(shí)下載多個(gè)包,而不是一個(gè)接一個(gè)地下載,這進(jìn)一步提高了安裝速度。
有關(guān)于“省”
上面提到,pnpm有一個(gè)全局的pnpm存儲(chǔ),所有的項(xiàng)目中軟鏈接、硬鏈接都是鏈接這里面的npm包這意味這在我們的電腦上對(duì)于同一個(gè)npm包我們只需要下載一次、存儲(chǔ)一次在我們?nèi)值膒npm存儲(chǔ)中,從而實(shí)現(xiàn)了對(duì)磁盤(pán)空間的”省“,不像之前使用npm或者yarn的時(shí)候,將所有的依賴安裝到項(xiàng)目文件夾的node_modules下,從而導(dǎo)致了在我們電腦上多次、重復(fù)的下載。
有關(guān)于“穩(wěn)”
- 基于鏈接的node_modules結(jié)構(gòu):pnpm通過(guò)軟鏈接將項(xiàng)目的依賴直接鏈接到虛擬store下對(duì)應(yīng)包的版本下,然后虛擬store對(duì)應(yīng)版本會(huì)直接硬鏈接到全局的pnpm store下。這種布局的一大好處是只有真正在依賴項(xiàng)中的包才能訪問(wèn),避免了幽靈依賴問(wèn)題。
- 嚴(yán)格的依賴平面:pnpm為每個(gè)包提供獨(dú)立的依賴視圖,減少了不必要的包冗余。這種嚴(yán)格的控制有助于防止版本沖突,確保每個(gè)包的依賴關(guān)系都被滿足,減少了因版本不匹配導(dǎo)致的問(wèn)題。
文章作者:JustHappy
原文地址:https://juejin.cn/post/7443866293755592742