pnpm實(shí)現(xiàn)依賴包共享和依賴包項(xiàng)目隔離的方法詳解
前言
pnpm(performant npm,意思是高性能的 npm)是 Node.js 的包管理器。它是 npm 的直接替代品,相對于npm和yarn它的優(yōu)點(diǎn)就在于速度快和高效節(jié)省磁盤空間。
本文主要講解pnpm相比于npm/yarn如何利用軟硬鏈接來節(jié)省磁盤空間,以及如何實(shí)現(xiàn)依賴包共享和依賴包項(xiàng)目隔離的。
硬鏈接
只能引用同一文件系統(tǒng)中的文件。硬鏈接引用的是文件在文件系統(tǒng)中的 物理索引(inode) 而inode存儲(chǔ)了文件的元數(shù)據(jù)和指向文件數(shù)據(jù)的指針。當(dāng)移動(dòng)或者刪除原始文件時(shí),硬鏈接不會(huì)被破壞,因?yàn)樗玫氖俏募膇node而不是文件在文件結(jié)構(gòu)中的位置,刪除或移動(dòng)原始文件只是改變了文件的結(jié)構(gòu)。硬鏈接記錄的是目標(biāo)的inode。同一文件的不同硬鏈接文件相當(dāng)于該文件的多個(gè)不同文件名,即多個(gè)不同訪問路徑,他們的inode都是一樣的。刪除一個(gè)硬鏈接并不會(huì)刪除文件的數(shù)據(jù),除非這是指向該文件的最后一個(gè)硬鏈接。只有當(dāng)所有指向文件的硬鏈接都被刪除后,該文件才會(huì)被標(biāo)記為可以刪除,并在適當(dāng)?shù)臅r(shí)候被文件系統(tǒng)清理。
不可以為目錄創(chuàng)建硬鏈接。因?yàn)檫@可能會(huì)造成循環(huán)引用的問題,假如目錄A硬鏈接到目錄B,而目錄B內(nèi)又包含了對目錄A的引用(可能是直接引用或者間接引用),那么就會(huì)形成一個(gè)循環(huán)。在遍歷目錄樹時(shí),系統(tǒng)可能會(huì)陷入這個(gè)無限循環(huán)中,導(dǎo)致無法正確定位到要訪問的目錄。
如何創(chuàng)建硬鏈接呢?
// 初始文件為file ln file hardfile // linux環(huán)境下創(chuàng)建file文件的硬鏈接 ln -ls // 查看文件信息
可以看到硬鏈接的文件和原始文件的inode值相同。
刪除原始文件后還可以訪問硬鏈接的文件:
rm file cat hardfile // 查看文件內(nèi)容 ls -li 查看該文件夾下所有文件的inode值
軟鏈接(符號(hào)鏈接)
和原文件不是同一個(gè)文件,符號(hào)鏈接會(huì)有自己的inode,它所引用的是原文件的path,當(dāng)原文件被移動(dòng)或刪除的時(shí)候,符號(hào)鏈接的文件也會(huì)失效。例如windows中的快捷方式就是一種軟鏈接。也可以為目錄創(chuàng)建軟連接。
ln -s file sysfile // linux環(huán)境下創(chuàng)建file文件的軟連接 ls -li
可以看到軟連接文件跟原始文件的inode值不同。
刪除原始文件后不能訪問軟鏈接的文件:
rm file cat sysfile
高效的節(jié)省磁盤空間
npm和yarn存在的問題
用 npm/yarn 的時(shí)候,如果 100 個(gè)項(xiàng)目都依賴 lodash,那么 lodash 很可能就被安裝了 100 次,磁盤中就有 100 個(gè)地方寫入了這部分代碼,這就會(huì)浪費(fèi)大量的磁盤空間。比較好的辦法就是每個(gè)包只在全局存儲(chǔ)一份,其它項(xiàng)目在使用時(shí)都通過引用去鏈接到這個(gè)包的地址,pnpm就是通過 內(nèi)部使用基于內(nèi)容尋址的文件系統(tǒng)來存儲(chǔ)磁盤上所有的文件來實(shí)現(xiàn)節(jié)省空間的。
pnpm安裝依賴結(jié)構(gòu)
我們借助pnpm官方給的例子進(jìn)行講解。假如我們的項(xiàng)目依賴了foo包,而foo包又依賴了bar包,那使用pnpm i的整個(gè)安裝流程是怎樣的,node_modules目錄的結(jié)構(gòu)又是怎樣的?
當(dāng)執(zhí)行pnpm i后首先會(huì)將依賴包下載到pnpm配置的本地倉庫里邊,可以使用pnpm config get store-dir命令查看,默認(rèn)是在用戶主文件夾中,可以通過pnpm config set store-dir ×××來手動(dòng)設(shè)置包下載后存放的目錄。下載到本地倉庫后,會(huì)在項(xiàng)目中的node_modules文件夾中創(chuàng)建一個(gè) .pnpm 文件夾,這個(gè)文件夾會(huì)將所有的依賴以及依賴的子依賴鋪平通過硬鏈接的方式引入進(jìn)來。
-> 表示硬鏈接 --> 表示符號(hào)鏈接(軟鏈接)
node_modules └── .pnpm ├── bar@1.0.0 │ └── node_modules │ └── bar -> <store-dir>/bar // 硬鏈接到本地store-dir倉庫中的bar文件 │ ├── index.js │ └── package.json └── foo@1.0.0 └── node_modules └── foo -> <store-dir>/foo // 硬鏈接到本地store-dir倉庫中的foo文件 ├── index.js └── package.json
它們是node_modules中唯一真實(shí)的文件。當(dāng)所有的依賴(包括依賴的子依賴)都硬鏈接到node_modules文件夾中的.pnpm目錄后就開始創(chuàng)建符號(hào)鏈接去構(gòu)建嵌套的依賴結(jié)構(gòu)。也就是在本例中的foo會(huì)依賴bar,那么就會(huì)在foo+@+版本號(hào)目錄下的node_modules目錄下創(chuàng)建bar依賴的符號(hào)鏈接去構(gòu)建嵌套結(jié)構(gòu)。
node_modules └── .pnpm ├── bar@1.0.0 │ └── node_modules │ └── bar -> <store-dir>/bar └── foo@1.0.0 └── node_modules ├── foo -> <store-dir>/foo └── bar --> ../../bar@1.0.0/node_modules/bar // 將嵌套依賴通過符號(hào)鏈接連接到真實(shí)的文件中
接下來就是去處理項(xiàng)目的直接依賴關(guān)系了,比如本例項(xiàng)目直接依賴了foo包
node_modules ├── foo --> ./.pnpm/foo@1.0.0/node_modules/foo 軟鏈接到.pnpm目錄下的真實(shí)文件 └── .pnpm ├── bar@1.0.0 │ └── node_modules │ └── bar -> <store-dir>/bar └── foo@1.0.0 └── node_modules ├── foo -> <store-dir>/foo └── bar --> ../../bar@1.0.0/node_modules/bar
可以看到.pnpm目錄中每個(gè)依賴包的目錄結(jié)構(gòu)都是固定的,每個(gè)依賴真實(shí)的文件(也就是該依賴的硬鏈接)都會(huì)放在一個(gè)包名+@+版本號(hào)的目錄的node_modules下,而該依賴的所有子依賴又通過符號(hào)鏈接放在同一層級下,這樣做的一個(gè)好處就是依賴包可以在代碼中引用自己。比如foo依賴包中的代碼中可以使用 const foo = require('foo') 這是沒問題的,因?yàn)閚ode查找依賴時(shí)就是通過node_modules一層一層去找到。
這種目錄結(jié)構(gòu)的另一個(gè)好處就是可以避免npm/yarn存在的幽靈依賴問題,在根目錄的node_modules文件夾中只存在項(xiàng)目的直接依賴包的符號(hào)鏈接,當(dāng)項(xiàng)目中的代碼使用這個(gè)包時(shí)會(huì)根據(jù)符號(hào)鏈接引用的地址找到.pnpm目錄中的真實(shí)文件。
驗(yàn)證一下,根目錄的node_modules文件夾中項(xiàng)目的直接依賴包的符號(hào)鏈接是指向.pnpm中的真實(shí)文件的
拿express包舉例,我們先在.pnpm目錄中找到express包的真實(shí)文件,然后再添加一行打印語句,那么當(dāng)根目錄的node_modules文件夾中express的符號(hào)鏈接中的文件也相應(yīng)改變就可以驗(yàn)證上面的結(jié)論。
express文件夾后邊的一個(gè)箭頭符號(hào)就表示該文件夾是一個(gè)符號(hào)鏈接,其實(shí)就是真實(shí)文件的一個(gè)快捷方式,當(dāng)真實(shí)文件里邊的內(nèi)容改變了,快捷方式內(nèi)的文件內(nèi)容肯定也跟著改變了。
因?yàn)閜npm是通過硬鏈接和符號(hào)鏈接去管理node_modules文件夾的,而不是像npm和yarn那樣直接將依賴包復(fù)制到node_modules中,所以可以高效的節(jié)省磁盤空間。
依賴共享
因?yàn)閜npm再安裝依賴包時(shí)是全局倉庫store-dir內(nèi)依賴包一個(gè)硬鏈接,那么其它的項(xiàng)目也都是對全局依賴中原始依賴包的一個(gè)硬鏈接,真實(shí)的文件只在磁盤中保留了一份,避免了多個(gè)項(xiàng)目帶來多份相同文件引起的空間浪費(fèi)問題。這就是多個(gè)項(xiàng)目中的依賴共享。
依賴包的項(xiàng)目隔離
但是說到硬鏈接,又有一個(gè)問題,這相當(dāng)于所有項(xiàng)目都依賴了同一個(gè)原始文件,那么在一個(gè)項(xiàng)目中修改了某個(gè)npm包的文件,就會(huì)影響到其他項(xiàng)目。但是我自己試了一下,開了兩個(gè)項(xiàng)目都依賴了express包,再其中一個(gè)項(xiàng)目修改后并不會(huì)影響到另外一個(gè)項(xiàng)目。經(jīng)過查閱,pnpm是使用了copy-on-write策略來解決這個(gè)問題的,實(shí)現(xiàn)依賴包的項(xiàng)目隔離。
copy-on-write(寫時(shí)復(fù)制)是一種優(yōu)化策略。這種策略的核心思想是在首次安裝或更新包時(shí),盡量使用硬鏈接和符號(hào)鏈接來引用全局存儲(chǔ)庫(store-dir)中的文件,以節(jié)省磁盤空間和提高安裝速度。但是,當(dāng)需要修改某個(gè)文件時(shí)(例如,需要打log去調(diào)試依賴包中的文件),
pnpm
不會(huì)直接修改硬鏈接或符號(hào)鏈接引用的原始文件,而是會(huì)創(chuàng)建一個(gè)該文件的副本,并將這個(gè)副本放入項(xiàng)目的node_modules
目錄中,相當(dāng)于修改的是原始文件的副本。這種做法的好處是避免了多個(gè)項(xiàng)目之間因?yàn)楣蚕砦募a(chǎn)生的潛在沖突。每個(gè)項(xiàng)目都有其自己的node_modules
目錄,其中包含它需要的所有依賴項(xiàng)和可能的文件副本,這些副本是獨(dú)立于其他項(xiàng)目的。這樣,即使一個(gè)項(xiàng)目更新了某個(gè)包,也不會(huì)影響到其他項(xiàng)目。
以上就是pnpm實(shí)現(xiàn)依賴包共享和依賴包項(xiàng)目隔離的方法詳解的詳細(xì)內(nèi)容,更多關(guān)于pnpm依賴包隔離和共享的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Node.js?操作本地文件及深入了解fs內(nèi)置模塊
這篇文章主要介紹了Node.js?操作本地文件及深入了解fs內(nèi)置模塊,node.js作為服務(wù)端應(yīng)用,肯定少不了對本地文件的操作,像創(chuàng)建一個(gè)目錄、創(chuàng)建一個(gè)文件、讀取文件內(nèi)容等都是我們開發(fā)中經(jīng)常需要用到的功能2022-09-09Node交互式的SFTP上傳實(shí)現(xiàn)過程剖析
這篇文章主要為大家介紹了Node交互式的SFTP上傳實(shí)現(xiàn)過程剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08輕松創(chuàng)建nodejs服務(wù)器(9):實(shí)現(xiàn)非阻塞操作
這篇文章主要介紹了輕松創(chuàng)建nodejs服務(wù)器(9):實(shí)現(xiàn)非阻塞操作,本系列文章會(huì)教你一步一步創(chuàng)建一個(gè)完整的服務(wù)器,要的朋友可以參考下2014-12-12詳解node中創(chuàng)建服務(wù)進(jìn)程
本篇文章主要介紹了詳解node中創(chuàng)建服務(wù)進(jìn)程,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-05-05基于Node實(shí)現(xiàn)可以操作MySQL的接口
這篇文章主要介紹了用Node寫個(gè)可以操作MySQL的接口,以前也用Node寫過接口,但不涉及數(shù)據(jù)庫操作,而我們發(fā)現(xiàn),后端寫接口,基本都繞不開數(shù)據(jù)庫操作,感覺不寫一個(gè)能操作數(shù)據(jù)庫的接口,就不算真正意義上學(xué)會(huì)了寫接口,那我們今天就學(xué)習(xí)一下,如何寫一個(gè)可以操作數(shù)據(jù)庫的接口2024-05-05npm?list輸出結(jié)果包含extraneous標(biāo)志記錄分析
這篇文章主要為大家介紹了npm?list輸出結(jié)果包含extraneous標(biāo)志記錄分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01