pnpm

1. 定义

pnpm 是 Node.js 生态下的一款包管理器,定位是 npm / yarn 的替代品。 两大卖点:省磁盘。通过内容寻址存储 (content-addressable store) + 硬链接实现全局包复用。

名字含义:performant npm。读音直接拼字母 P-N-P-M,或读 “perf-npm”。

2. pnpm 解决了什么问题

传统 npm/yarn 的问题:

问题npm/yarnpnpm
磁盘占用每个项目都拷一份 node_modules,10 个项目用 lodash 就存 10 份全局 store 只存一份,项目里用硬链接 + 软链指过去
幽灵依赖 (phantom deps)扁平化 node_modules,导致没在 package.json 里声明的包也能被 import严格结构,只能 import 声明过的包
Peer 依赖警告满天飞自动装 peer deps,版本冲突时报错默认不自动装,用 --shamefully-hoist 或配置控制
安装速度慢,要拷大量文件快,硬链接几乎不增加磁盘 IO

3. 核心机制:Content-Addressable Store + Symlinked node_modules

全局 store (~/.pnpm-store/)
  └── lodash@4.17.21/        ← 真正的文件只存一份

项目 node_modules/
  ├── .pnpm/                  ← 中间层,硬链接到全局 store
  │   └── lodash@4.17.21/
  │       └── node_modules/lodash/
  └── lodash -> .pnpm/lodash@4.17.21/node_modules/lodash   ← 软链给你的代码用

关键点:

  • 全局 store:所有包按 包名@版本/hash 存一份,跨项目复用
  • 硬链接:项目里的包文件硬链到 store,不占额外磁盘
  • 软链接结构node_modules 里只暴露 package.json 声明的包,杜绝幽灵依赖

这就是为什么 pnpm 装一个新项目几乎不增加磁盘占用——大部分包 store 里早有了。

4. 常用命令

基础(和 npm 几乎一样)

pnpm install                 # 安装 package.json 所有依赖(alias: pnpm i)
pnpm add express             # 添加生产依赖
pnpm add -D vitest           # 添加开发依赖
pnpm add -g pm2              # 全局安装
pnpm remove express          # 卸载
pnpm update                  # 升级所有依赖
pnpm update express          # 只升级 express

运行脚本

pnpm run build               # 跑 package.json 里的 scripts.build
pnpm build                   # run 可省略
pnpm test                    # 等于 pnpm run test
pnpm start                   # 等于 pnpm run start

查询

pnpm list                    # 列出依赖树(alias: pnpm ls)
pnpm list --depth 1          # 只看一层
pnpm why lodash              # 看 lodash 是被谁依赖进来的
pnpm outdated                # 看哪些包过期了

Workspace(monorepo 多包管理)

pnpm init                    # 初始化 package.json
pnpm init --shamefully-hoist # 兼容老项目:扁平化 node_modules(像 npm 那样)
pnpm -r add lodash           # 给 workspace 里所有包都装 lodash
pnpm --filter webapp add axios   # 只给名为 webapp 的子包装 axios
pnpm -r run build            # 在所有包里跑 build 脚本

workspace 配置(根 pnpm-workspace.yaml):

packages:
  - 'packages/*'
  - 'apps/*'

其他实用

pnpm exec tsc                # 在项目环境里运行本地装的二进制
pnpm dlx create-vite         # 临时运行一个包,不安装到项目(类似 npx)
pnpm store prune             # 清理 store 里没被任何项目引用的包
pnpm pack                    # 打包成 .tgz

5. pnpm vs npm vs yarn 速查

维度npmyarn (classic)pnpm
安装速度
磁盘占用高(每项目一份)低(全局复用)
node_modules 结构扁平扁平严格分层(软链)
幽灵依赖
Lockfilepackage-lock.jsonyarn.lockpnpm-lock.yaml
Workspaces支持支持支持(更灵活)
Plug’n’Play不支持yarn 2+ PnP不支持(用软链方案)
缓存机制本地 cache本地 cache全局 store + 硬链接

6. 典型工作流

新项目

pnpm create vite my-app --template react-ts
cd my-app
pnpm install
pnpm dev

接手已有项目

git clone <repo>
cd <repo>
pnpm install                 # 自动识别 lockfile,按锁定的版本装

Monorepo(多包仓库)

mkdir monorepo && cd monorepo
pnpm init
echo "packages:\n  - 'packages/*'" > pnpm-workspace.yaml
mkdir -p packages/webapp packages/utils
pnpm --filter webapp add express
pnpm -r run build

7. 常见坑

  • 某些工具找不到包:依赖了扁平化 node_modules 的老工具会失败。解决:pnpm install --shamefully-hoist,或针对性配置 node-linker=hoisted
  • pnpm dlx vs npx:用法一样,但 pnpm 的 dlx 也会走全局 store,重复运行更快
  • Peer 依赖冲突:pnpm 默认不自动装 peer deps,会报警告。需要的话手动 pnpm add 那个 peer 包,或在 .npmrc 配置 auto-install-peers=true
  • node_modules/.pnpm 不要手动改:这是 pnpm 的内部结构,改了会出诡异问题
  • 硬链接跨磁盘失效:如果 store 和项目不在同一磁盘分区,硬链接会降级为拷贝,磁盘优势就没。Apple Silicon 默认 store 在 ~/.pnpm-store,跨盘用户需配置 store-dir

8. 延伸阅读 / 关联概念

  • Corepack — Node 自带的包管理器版本管理工具,corepack enable pnpm 即可用,不用全局装 pnpm
  • 幽灵依赖 (Phantom Dependencies) — 扁平 node_modules 导致能 import 未声明包的问题,pnpm 用软链结构解决
  • Monorepo 工具对比 — pnpm workspaces / Nx / Turborepo,pnpm 自带 workspace 就够大多数场景用
  • 官方文档:https://pnpm.io/