pnpm — — 目前最好的 package manager,沒有之一

xxlee (Ching Hung Lee)
6 min readDec 5, 2022

用一句話快速介紹 pnpm:

透過非扁平化的 node_modules 結構,達到「快速」,「安全」,「省空間」的開發體驗。

非扁平化的 node_modules 結構

原文章:Flat node_modules is not the only way

使用 npm 和 pnpm 分別安裝 express,觀察各自的 node_modules 結構

npm

.bin
accepts
array-flatten
body-parser
bytes
content-disposition
cookie-signature
cookie
debug
depd
destroy
ee-first
encodeurl
escape-html
etag
express

pnpm

.pnpm
.modules.yaml
express

可以發現 npm 將所有的依賴扁平的攤開在 node_modules 中,而 pnpm 則只保留了我們實際安裝的 express

那麼模組需要的依賴去哪裡了呢?答案藏在 .pnpm 這個資料夾,所有的依賴會被扁平的存在這裡,而 express 只是一個軟連結(symbolic link),會連至 .pnpm 中對應的 package。package 的實際位置可以用以下規則找到:

.pnpm/<模組名稱>@<模組版本>/node_modules/<模組名稱>
觀察下方 path bar 的變化

進到 express 的資料夾後可以發現仍然沒有 node_modules ,這是因為 pnpm 會將模組本身和依賴模組放在同一位置,即 .pnpm/<模組名稱>@<模組版本>/node_modules ,將依賴放在同一層可避免軟連結循環(cicular symlinks)

以上就是 pnpm 和傳統扁平的資料夾結構最大的差別,當然在實務上還有許多困難要克服,可以參考以下連結:

Performance

https://pnpm.io/benchmarks,每日更新

相比於傳統的安裝流程,pnpm 能夠將每個模組獨立成各自的 stage,彼此不會互相 blocking

傳統的安裝方式
pnpm 將每個模組獨立

回顧三大優點

快速

1. pnpm 在本機維護一個 global store,若要安裝的模組曾經在這台電腦上被安裝過,則 pnpm 會直接去 global store 透過硬連結的方式安裝,而不是從網路上下載,為何不使用軟連結直接連到 global store 可參考這個回答。global store 的位置如下:

Linux   : ~/.local/share/pnpm/store (default)
Windows : C:\Users\YOUR_NAME\AppData\Local\pnpm\store
macOS : ~/Library/pnpm/store

# Or you can find path by
pnpm store path

2. 每個模組獨立的 installation stage

節省空間

  1. 在不同 repo 使用到相同模組時,都是透過硬連結去存取 global store,意即每個模組在硬碟只會佔用一次空間(雖然看起來像各自佔了一次空間
  2. 同時依賴於不同版本的相同模組,只有存在差異的檔案才會被新增至儲存庫中

安全

這裡的安全指的是幽靈依賴(phantom dependencies)的問題,npm 和 yarn 這類扁平化的資料夾結構,在安裝「帶有 B 模組依賴」的 「A 模組」時,在程式碼寫 import something from 'B'; 是可以成功編譯的。而 pnpm 的 node_modules 結構則只會列出實際安裝的模組,意即你只能 import 有顯式安裝的模組,有效杜絕這個問題的發生。

其他優點

  1. 原生支援 monorepo,雖然還無法完全覆蓋 lerna 的場景,但以套件的更新速度,是個非常有競爭力的選擇
  2. 管理 Node 版本

Migrate from npm / yarn

由於 pnpm 生成的目錄結構完全相容於 Node,最簡單的方式就是刪掉node_modules ,以及 package-lock.jsonyarn.lock ,然後直接重新 pnpm install 即可,但這樣有可能會造成模組版號的改動,因此更安全的做法是用:

pnpm import

此指令能夠將原有的 lock file 轉換成 pnpm-lock.yaml官網說明)。轉換完成後再執行 pnpm install 即可

.yarnrc

常用於定義 private package,記得將檔名改成 .npmrc

幽靈依賴

記得上面提到安全的優點嗎?在 pnpm 無法 import 不存在 dependencies list 的模組,這在轉換時就有可能發生問題,但解決方式也比較簡單,通常在 build 時就會直接噴錯,再把對應的模組 pnpm install 上去即可

CI

在本機成功將服務跑起來後,別忘了也要去調整 CI script 的寫法,官方有對常見的 CI 提供 sample script,基本上和 npm yarn 大同小異

Node version

pnpm 各版本對 Node 版本的支援可參考這裡

小結

綜合上述優點,目前社群普遍的共識 pnpm 是目前最好的 package manager,且套件幾乎一個禮拜就會更新一次(Release Log),代表問題修復和新功能推出的速度非常快。新開的 repo 可以無腦直接無腦選用,舊有的 repo 雖然開發體驗的提升不會像從 webpack 換到 vite 這麼巨大,但如果所有 repo 都用 pnpm 來管理,global store 節省空間和安裝速度的優勢就會非常明顯。

--

--