一、安裝
npm i -g pnpm
二、特性
- 速度快
- 高效使用磁盤空間
- pnpm 內部使用基於內容尋址的文件系統來存儲磁盤上所有的文件,這個文件系統出色的地方在於:
- 不會重復安裝同一個包。用 npm/yarn 的時候,如果 100 個項目都依賴 lodash,那么 lodash 很可能就被安裝了 100 次,磁盤中就有 100 個地方寫入了這部分代碼。但在使用 pnpm
只會安裝一次,磁盤中只有一個地方寫入,后面再次使用都會直接使用 hardlink。symlink和hardlink機制 (軟鏈接和硬鏈接) - 即使一個包的不同版本,pnpm 也會極大程度地復用之前版本的代碼。舉個例子,比如 lodash 有 100 個文件,更新版本之后多了一個文件,那么磁盤當中並不會重新寫入 101 個文件,而是保留原來的 100 個文件的
hardlink,僅僅寫入那一個新增的文件。
- 對monorepo的支持
- 安全性
- 安全 pnpm通過特殊文件,來對包含所有已安裝軟件包進行校驗,保證軟件包的完整性。
- 離線模式,將所有下載的包保存在本地注冊表鏡像中。當本地可用就不用發送http請求
三、依賴管理
- npm yarn v1 流程
- 將依賴包的版本區間解析為某個具體的版本號
- 下載對應版本依賴的 tar 包到本地離線鏡像
- 將依賴從離線鏡像解壓到本地緩存
- 將依賴從緩存拷貝到當前目錄的 node_modules 目錄
node_modules
└─ foo
├─ index.js
├─ package.json
└─ node_modules
└─ bar
├─ index.js
└─ package.json
- npm yarn v1 缺陷
- 依賴層級太深,會導致路徑過程,win更加明顯
- 大量包被重復安裝,文件體積過大。許多包都依賴其中一個工具的依賴包
- 模塊實例不能共享。React內部變量,在兩個不同包引入的 React 不是同一個模塊實例,因此無法共享內部變量,導致一些不可預知的 bug。
- npm v3版本
- 引入扁平化依賴管理
- 所有的依賴都被拍平到node_modules目錄下,不再有很深層次的嵌套關系。這樣在安裝新的包時,根據 node require
機制,會不停往上級的node_modules當中去找,如果找到相同版本的包就不會重新安裝,解決了大量包重復安裝的問題,而且依賴層級也不會太深。
node_modules
├─ foo
| ├─ index.js
| └─ package.json
└─ bar
├─ index.js
└─ package.json
- yarn和npm v3 同樣使用了扁平化的node_module結構,但是也帶來許多問題。
- 模塊可以訪問他們不依賴的包
- 扁平化依賴關系樹的算法十分復雜
- 某些包必須復制一個項目的node_modules文件夾
- 硬盤空間問題
- pnpm
- 通過軟連接方式去獲取本地倉庫store里面包,節省空間,防止Phantom dependencies(幽靈依賴)和 NPM doppelgangers (npm 二重身)
四、symlink和hardlink機制 (軟鏈接和硬鏈接)
- hardlink和symlink區別
- hardlink僅能鏈接文件,而symlink可以鏈接目錄
- hardlink在鏈接完成后僅和文件內容關聯,和之前鏈接的文件沒有任何關系。而符號鏈接始終和之前鏈接的文件關聯,和文件內容不直接相關
- pnpm鏈接依賴流程
- pnpm 是通過 hardlink 在全局里面 store 目錄來存儲 node_modules 依賴里面的 hardlink 地址,然后在引用依賴的時候則是通過 symlink 去找到對應虛擬磁盤目錄下(.pnpm 目錄)的依賴地址。
- 一般 store 目錄默認是設置在 ${os.homedir}/.pnpm-store 這個目錄下
node_modules
└── bar // symlink to .pnpm/bar@1.0.0/node_modules/bar
└── foo // symlink to .pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>/bar
│ ├── index.js
│ └── package.json
└── foo@1.0.0
└── node_modules
└── foo -> <store>/foo
├── index.js
└── package.json
五、對於Monorepo解決了 Phantom dependencies(幽靈依賴) 和 NPM doppelgangers (npm 二重身)
- Phantom dependencies
- packge.json
沒有這個問題但是用戶卻能用到這個包。主要是node_modules結構的問題,主要是依賴里面有foo,然后foo又依賴了bar,然后yarn對安裝進行扁平化結構處理,會把node_modules進行打平foo和bar出現在同一層級根據node尋徑原理,用戶能require到foo也能require到bar,這就導致幽靈依賴,如果哪天版本變動,那個包版本變動就會導致報錯。在pnpm就不會存在這種問題,他會在虛擬磁盤去尋找,用戶required不到。
- NPM doppelgangers
- 這個問題其實是hoist導致的。大量依賴被重復安裝。
- 例如有個 package,下面依賴有 lib_a、lib_b、lib_c、lib_d,其中 a 和 b 依賴 util_e@1.0.0,而 c 和 d 依賴 util_e@2.0.0。
- package
- package.json
- node_modules
- lib_a
- node_modules <- util_e@1.0.0
- lib_b
- node_modules <- util_e@1.0.0
_ lib_c
- node_modules <- util_e@2.0.0
- lib_d
- node_modules <- util_e@2.0.0
- 這樣必然會導致很多依賴被重復安裝,於是就有了 hoist 和打平依賴的操作:
- package
- package.json
- node_modules
- util_e@1.0.0
- lib_a
- lib_b
_ lib_c
- node_modules <- util_e@2.0.0
- lib_d
- node_modules <- util_e@2.0.0
-
但是這樣也只能提升一個依賴,如果兩個依賴都提升了會導致沖突,這樣同樣會導致一些不同版本的依賴被重復安裝多次,這里就會導致使用 npm 和 yarn 的性能損失。
-
如果是 pnpm 的話,這里因為依賴始終都是存在 store 目錄下的 hard links ,一份不同的依賴始終都只會被安裝一次,因此這個是能夠被徹徹底底的消除的。
六、pnpm可能出現的問題
- pnpm 對Electron存在問題 出現符號鏈接問題。
- pnpm store 問題 本地包體積過大
- pnpm會下載包保存在本地,時間長了導致本地store文件過大,可以通過減少重復不被引入的包的方式 foo@0.01 foo@1.0.0 你可以通過pnpm store prune
來清理無用的包,當再次需要他們重新下載。不建議過於頻繁。