17.6K Star! 一款快速高效的包管理工具
簡介
pnpm 是一個快速的、節(jié)省磁盤空間的包管理工具。pnpm 使用內(nèi)容尋址的文件系統(tǒng)來存儲磁盤上的所有模塊文件
項目地址
https://github.com/pnpm/pnpm
安裝
- 使用 npm
npm install -g pnpm
- 使用 Homebrew如果你已經(jīng)安裝了 Homebrew 軟件包管理器,則可以使用如下命令賴安裝 pnpm:
brew install pnpm
兼容性
下圖列出了以往的 pnpm 版本和對應(yīng)支持的 Node.js 版本
命令
以下是與 npm 等價命令的對照表:
npm 命令 | pnpm 等價命令 |
npm install | pnpm install |
npm i | pnpm add |
npm run | pnpm |
特點
- 速度快pnpm 相較于 npm/yarn 兩個常用的包管理工具性能得到大幅提升,下圖為官方提供的基準(zhǔn)測試數(shù)據(jù)
節(jié)省磁盤空間pnpm 內(nèi)部使用內(nèi)容尋址的文件系統(tǒng)來存儲磁盤上所有的文件
1、使用 npm/yarn 時,如果你有 100 個項目,并且所有項目都有一個相同的依賴包,那么, 你在硬盤上就需要保存 100 份該相同依賴包的副本,但是pnpm會只在一個地方寫入這部分代碼,后面使用會直接使用hard link,不會占用額外的磁盤空間
2、當(dāng)這個依賴包需要更新到新版本時,并且新版本中只有一個文件有修改。pnpm 并不會重新寫入101個文件,而是保留原來的 100 個文件的 hard link,僅僅寫入那一個新增的文件到存儲中
依賴管理
pnpm 采用Hard link? (硬鏈接)和 Symbolic Link(符號鏈接)來創(chuàng)建依賴關(guān)系的嵌套結(jié)構(gòu)
假設(shè)安裝foo@1.0.0?依賴于bar@1.0.0?, pnmp 會將這兩個包硬鏈接到node_modules :
node_modules
└── .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
一旦所有的包都被硬鏈接到node_modules ,符號鏈接就會被用來構(gòu)建嵌套依賴關(guān)系圖
你可能已經(jīng)注意到了,這兩個包都硬鏈接到一個node_modules? 文件夾 ( foo@1.0.0/node_modules/foo)的子文件夾中。這是為了:
- 允許包能import自身。 foo 可以require('foo/package.json')或import * as package from "foo/package.json"
- 避免循環(huán)符號鏈接。 包的依賴關(guān)系被放置在依賴包所在文件夾中。對于 Node.js 來說,依賴項是放在在包的node_modules中還是在某個父級目錄的node-modules中是沒有區(qū)別的
下一步,安裝符號鏈接依賴。bar? 被符號鏈接到foo@1.0.0/node_modules文件夾:
node_modules
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>/bar
└── foo@1.0.0
└── node_modules
├── foo -> <store>/foo
└── bar -> ../../bar@1.0.0/node_modules/bar
接下來,處理直接依賴關(guān)系。foo? 將被符號鏈接到node_modules?根文件夾,因為foo是項目的依賴項:
node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ └── bar -> <store>/bar
└── foo@1.0.0
└── node_modules
├── foo -> <store>/foo
└── bar -> ../../bar@1.0.0/node_modules/bar
這是一個非常簡單的例子。 不過,無論依賴項的數(shù)量和依賴關(guān)系圖的深度如何,布局都會保持這種結(jié)構(gòu)。
如果這時,添加qar@2.0.0為bar和foo的依賴項:
node_modules
├── foo -> ./.pnpm/foo@1.0.0/node_modules/foo
└── .pnpm
├── bar@1.0.0
│ └── node_modules
│ ├── bar -> <store>/bar
│ └── qar -> ../../qar@2.0.0/node_modules/qar
├── foo@1.0.0
│ └── node_modules
│ ├── foo -> <store>/foo
│ ├── bar -> ../../bar@1.0.0/node_modules/bar
│ └── qar -> ../../qar@2.0.0/node_modules/qar
└── qar@2.0.0
└── node_modules
└── qar -> <store>/qar
如你所見,即使圖現(xiàn)在更深(foo > bar > qar),文件系統(tǒng)中的目錄深度仍然相同。
這種布局乍一看可能很奇怪,但其實它是完全兼容 Node 的模塊解析算法的!node_modules 根目錄中的包只是一個符號鏈接。require('foo')? 將執(zhí)行 node_modules/.pnpm/foo@1.0.0/node_modules/foo/indexjs? 中的文件(這里是硬鏈接),而不是 node_modules/foo/index.js 中的文件。
嚴(yán)格性
這種布局的一大好處是只有真正在依賴項中的包才能訪問。如果是使用扁平化的 node_modules 結(jié)構(gòu),程序可以訪問到一些不在package.json依賴中的包。這可以避免一些愚蠢的錯誤