一:簡單介紹
pnpm 是一個速度快、節省磁盤空間的軟件包管理器。pnpm 在功能上類似於 npm 和 Yarn 。
pnpm文檔手冊學習與信息參考網址:https://www.pnpm.cn/
安裝教程
npm i -g pnpm
安裝第三方模塊
// 安裝 axios 默認添加至 dependencies pnpm install axios // 安裝 axios 並將 axios 添加至 devDependencies pnpm install axios -D // 安裝 axios 並將 axios 添加至 dependencies pnpm install axios -S
最新第三方模塊
pnpm update
卸載第三方模塊
// 移除 axios pnpm uninstall axios
將本地項目連接到另一個項目。
注意,使用的是硬鏈接,而不是軟鏈接。
pnpm link ../../axios
二、特性概覽
1. 速度快
pnpm 安裝包的速度究竟有多快?先以 React 包為例來對比一下:
可以看到,作為黃色部分的 pnpm,在絕多大數場景下,包安裝的速度都是明顯優於 npm/yarn,速度會比 npm/yarn 快 2-3 倍。
對 yarn 比較熟悉的同學可能會說,yarn 不是有 PnP 安裝模式嗎?直接去掉 node_modules,將依賴包內容寫在磁盤,節省了 node 文件 I/O 的開銷,這樣也能提升安裝速度。(具體原理見這篇文章)
https://loveky.github.io/2019/02/11/yarn-pnp/
接下來,我們以這樣一個倉庫為例,我們來看一看 benchmark 數據,主要對比一下 pnpm
和 yarn PnP
:
從中可以看到,總體而言,pnpm
的包安裝速度還是明顯優於 yarn PnP
的。
2. 高效利用磁盤空間
pnpm 內部使用基於內容尋址
的文件系統來存儲磁盤上所有的文件,這個文件系統出色的地方在於:
-
不會重復安裝同一個包。用 npm/yarn 的時候,如果 100 個項目都依賴 lodash,那么 lodash 很可能就被安裝了 100 次,磁盤中就有 100 個地方寫入了這部分代碼。但在使用 pnpm 只會安裝一次,磁盤中只有一個地方寫入,后面再次使用都會直接使用
hardlink
-
即使一個包的不同版本,pnpm 也會極大程度地復用之前版本的代碼。舉個例子,比如 lodash 有 100 個文件,更新版本之后多了一個文件,那么磁盤當中並不會重新寫入 101 個文件,而是保留原來的 100 個文件的
hardlink
,僅僅寫入那一個新增的文件
。
3. 支持 monorepo
隨着前端工程的日益復雜,越來越多的項目開始使用 monorepo。之前對於多個項目的管理,我們一般都是使用多個 git 倉庫,但 monorepo 的宗旨就是用一個 git 倉庫來管理多個子項目,所有的子項目都存放在根目錄的packages
目錄下,那么一個子項目就代表一個package
。
pnpm 與 npm/yarn 另外一個很大的不同就是支持了 monorepo,體現在各個子命令的功能上,比如在根目錄下 pnpm add A \-r
, 那么所有的 package 中都會被添加 A 這個依賴,當然也支持 --filter
字段來對 package 進行過濾。
4. 安全性高
之前在使用 npm/yarn 的時候,由於 node_module 的扁平結構,如果 A 依賴 B, B 依賴 C,那么 A 當中是可以直接使用 C 的,但問題是 A 當中並沒有聲明 C 這個依賴。因此會出現這種非法訪問的情況。但 pnpm 腦洞特別大,自創了一套依賴管理方式,很好地解決了這個問題,保證了安全性,具體怎么體現安全
、規避非法訪問依賴的風險
的。
不知道你發現沒有,pnpm 這種依賴管理的方式也很巧妙地規避了非法訪問依賴
的問題,也就是只要一個包未在 package.json 中聲明依賴,那么在項目中是無法訪問的。
但在 npm/yarn 當中是做不到的,那你可能會問了,如果 A 依賴 B, B 依賴 C,那么 A 就算沒有聲明 C 的依賴,由於有依賴提升的存在,C 被裝到了 A 的node_modules
里面,那我在 A 里面用 C,跑起來沒有問題呀,我上線了之后,也能正常運行啊。不是挺安全的嗎?
還真不是。
第一,你要知道 B 的版本是可能隨時變化的,假如之前依賴的是C@1.0.1
,現在發了新版,新版本的 B 依賴 C@2.0.1
,那么在項目 A 當中 npm/yarn install 之后,裝上的是 2.0.1 版本的 C,而 A 當中用的還是 C 當中舊版的 API,可能就直接報錯了。
第二,如果 B 更新之后,可能不需要 C 了,那么安裝依賴的時候,C 都不會裝到node_modules
里面,A 當中引用 C 的代碼直接報錯。
還有一種情況,在 monorepo 項目中,如果 A 依賴 X,B 依賴 X,還有一個 C,它不依賴 X,但它代碼里面用到了 X。由於依賴提升的存在,npm/yarn 會把 X 放到根目錄的 node_modules 中,這樣 C 在本地是能夠跑起來的,因為根據 node 的包加載機制,它能夠加載到 monorepo 項目根目錄下的 node_modules 中的 X。但試想一下,一旦 C 單獨發包出去,用戶單獨安裝 C,那么就找不到 X 了,執行到引用 X 的代碼時就直接報錯了。
這些,都是依賴提升潛在的 bug。如果是自己的業務代碼還好,試想一下如果是給很多開發者用的工具包,那危害就非常嚴重了。
npm 也有想過去解決這個問題,指定--global-style
參數即可禁止變量提升,但這樣做相當於回到了當年嵌套依賴的時代,一夜回到解放前,前面提到的嵌套依賴的缺點仍然暴露無遺。
npm/yarn 本身去解決依賴提升的問題貌似很難完成,不過社區針對這個問題也已經有特定的解決方案: dependency-check,地址:
https://github.com/dependency-check-team/dependency-check
但不可否認的是,pnpm 做的更加徹底,獨創的一套依賴管理方式不僅解決了依賴提升的安全問題,還大大優化了時間和空間上的性能。