pnpm

pnpm(Performant npm)是高效的 Node.js 包管理器,通过内容寻址存储 + 硬链接避免重复安装相同依赖,显著节省磁盘空间并加快安装速度。同时采用严格的依赖隔离,消除「幽灵依赖」问题。

同类工具:npm(官方默认)、yarn(lockfile 稳定)。


安装

# 推荐:通过 npm 全局安装
npm install -g pnpm
 
# 或通过独立安装脚本(不依赖现有 Node/npm)
curl -fsSL https://get.pnpm.io/install.sh | sh -
 
# 升级 pnpm 自身
pnpm self-update
 
# 验证
pnpm --version

核心原理

内容寻址存储(CAS)

所有下载的包存储在全局 store(~/.pnpm-store),按包内容的哈希值索引。同一个包的同一版本在磁盘上只存一份,项目的 node_modules 通过硬链接指向 store:

~/.pnpm-store/               ← 全局缓存(内容寻址)
  v3/
    files/
      00/abc123...            ← 文件按哈希存储

project-a/node_modules/      ← 硬链接,指向 store
project-b/node_modules/      ← 同一份文件,无重复占用

严格依赖隔离

npm / yarn 的 node_modules 扁平化结构允许代码访问未在 package.json 中声明的包(幽灵依赖)。pnpm 采用符号链接结构,只有显式声明的依赖可被访问:

node_modules/
├── .pnpm/                   ← 实际包文件(隔离)
│   ├── express@4.18.2/
│   └── lodash@4.17.21/
├── express -> .pnpm/express@4.18.2/node_modules/express
└── lodash  → .pnpm/lodash@4.17.21/node_modules/lodash

基本命令

# 安装所有依赖
pnpm install
pnpm i                      # 缩写
 
# 安装生产依赖
pnpm add express
pnpm add express@4.18.2     # 指定版本
 
# 安装开发依赖
pnpm add -D typescript jest
pnpm add --save-dev eslint
 
# 安装全局工具
pnpm add -g nodemon
pnpm add -g typescript
 
# 卸载
pnpm remove express
pnpm remove -D jest
 
# 更新依赖
pnpm update                 # 更新所有(在版本范围内)
pnpm update express         # 更新指定包
pnpm update --latest        # 忽略版本范围,升级到最新

运行脚本

pnpm run dev
pnpm run build
pnpm run test
pnpm test                   # test / start / install 可省略 run
pnpm start
 
# 传递额外参数
pnpm run test -- --coverage

pnpm-lock.yaml

pnpm 使用 pnpm-lock.yaml 作为锁文件(类似 package-lock.json),必须提交到 Git,确保团队和 CI 安装结果一致。

# CI 环境:严格按锁文件安装,不更新
pnpm install --frozen-lockfile
 
# 验证 lockfile 是否与 package.json 同步
pnpm install --frozen-lockfile --check-if-lockfile-empty

Monorepo(工作区)

pnpm 内置 workspace 支持,是 Monorepo 的主流选择(Turborepo、Nx 均推荐 pnpm):

配置

根目录创建 pnpm-workspace.yaml

packages:
  - 'packages/*'
  - 'apps/*'
  - '!**/__tests__/**'    # 排除测试目录

项目结构示例:

monorepo/
├── pnpm-workspace.yaml
├── package.json           # 根 package(通常不发布)
├── packages/
│   ├── ui/                # 共享组件库
│   │   └── package.json   # name: "@myorg/ui"
│   └── utils/             # 工具函数
│       └── package.json   # name: "@myorg/utils"
└── apps/
    ├── web/               # 前端应用
    └── api/               # 后端服务

工作区命令

# 在所有包中执行命令
pnpm -r run build           # --recursive 的缩写
 
# 在指定包中执行
pnpm --filter @myorg/ui run build
pnpm --filter web run dev
 
# 按拓扑顺序构建(先构建被依赖的包)
pnpm -r --sort run build
 
# 工作区内包互相引用(使用 workspace: 协议)
# apps/web/package.json
{
  "dependencies": {
    "@myorg/ui": "workspace:*"    # 指向本地包
  }
}

工作区安装:

# 在根目录安装所有 workspace 的依赖
pnpm install
 
# 只给某个包添加依赖
pnpm add lodash --filter @myorg/utils

.npmrc 配置

pnpm 复用 .npmrc 格式,支持额外配置项:

# 镜像源
registry=https://registry.npmmirror.com
 
# 全局 store 路径(可自定义)
store-dir=~/.pnpm-store
 
# 提升特定包到根 node_modules(兼容某些工具的路径假设)
public-hoist-pattern[]=*eslint*
public-hoist-pattern[]=*prettier*
 
# 严格模式:禁止访问未声明的依赖(默认已严格,此项用于调试)
strict-peer-dependencies=false
 
# 自动安装 peerDependencies(pnpm 8+ 默认 true)
auto-install-peers=true

全局 store 管理

# 查看 store 路径
pnpm store path
 
# 清理孤立包(无任何项目引用的缓存)
pnpm store prune
 
# 验证 store 完整性
pnpm store verify

pnpm vs npm vs yarn 对比

特性npmyarnpnpm
安装速度最快
磁盘占用最高最低
幽灵依赖存在存在严格隔离
Monorepo基础支持Workspaces原生优秀支持
锁文件package-lock.jsonyarn.lockpnpm-lock.yaml
生态兼容最广广广(少数工具需配置)

常用命令速查

pnpm install                # 安装所有依赖
pnpm add <pkg>              # 安装生产依赖
pnpm add -D <pkg>           # 安装开发依赖
pnpm add -g <pkg>           # 全局安装
pnpm remove <pkg>           # 卸载
pnpm update                 # 更新依赖
pnpm run <script>           # 运行脚本
pnpm -r run <script>        # 所有工作区运行脚本
pnpm --filter <pkg> <cmd>   # 在指定工作区执行
pnpm list                   # 列出已安装包
pnpm outdated               # 查看可升级包
pnpm store prune            # 清理缓存

相关链接

  • npm — Node.js 默认包管理器,命令基本兼容
  • yarn — 替代包管理器(Berry 版本也支持 workspaces)
  • Node — Node.js 运行时基础
  • nvm — Node.js 版本管理