17.6K Star! 一款快速高效的包管理工具
簡(jiǎn)介
pnpm 是一個(gè)快速的、節(jié)省磁盤空間的包管理工具。pnpm 使用內(nèi)容尋址的文件系統(tǒng)來存儲(chǔ)磁盤上的所有模塊文件
項(xiàng)目地址
https://github.com/pnpm/pnpm
安裝
- 使用 npm
npm install -g pnpm
- 使用 Homebrew如果你已經(jīng)安裝了 Homebrew 軟件包管理器,則可以使用如下命令賴安裝 pnpm:
brew install pnpm
兼容性
下圖列出了以往的 pnpm 版本和對(duì)應(yīng)支持的 Node.js 版本

命令
以下是與 npm 等價(jià)命令的對(duì)照表:
npm 命令 | pnpm 等價(jià)命令 |
npm install | pnpm install |
npm i | pnpm add |
npm run | pnpm |
特點(diǎn)
- 速度快pnpm 相較于 npm/yarn 兩個(gè)常用的包管理工具性能得到大幅提升,下圖為官方提供的基準(zhǔn)測(cè)試數(shù)據(jù)

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

































