pnpm logo

pnpm

高性能、节省磁盘空间的 Node.js 包管理器

精选工具包管理器高性能节省磁盘Monorepo

pnpm 通过内容寻址存储和硬链接机制消除重复依赖,安装速度比 npm 快约 3 倍,磁盘占用大幅减少。v11 引入 SQLite 索引和供应链安全增强,是 Monorepo 的最佳选择。

pnpm

高性能、节省磁盘空间的 Node.js 包管理器

技术简介说明

pnpm(performant npm)是一款面向 Node.js 生态的高性能包管理器,由 Zoltan Kochan 于 2015 年创建,2024 年后逐步过渡到社区驱动的治理模式。它在功能层面完全兼容 npm 的 package.json 生态与 registry 协议,但通过对内容寻址存储(Content-Addressable Store)硬链接符号链接三者的协同设计,从底层重构了依赖安装与磁盘占用的方式。

传统 npm(v2 之前)将每个项目的依赖完整拷贝到 node_modules/ 目录,同一台机器上安装十次 lodash 就会在磁盘上出现十份完全相同的副本。Yarn(Classic)通过全局缓存避免了重复下载,但安装时仍然把缓存内容拷贝到项目目录。pnpm 的做法则更进一步:所有从 registry 下载的文件会被统一存储在一个全局的内容寻址仓库(通常位于 ~/.local/share/pnpm/store/),文件名以其内容哈希命名;项目安装时,pnpm 通过硬链接将该仓库中的文件直接映射到项目的 node_modules/.pnpm/ 目录,而非拷贝。这意味着同一份 [email protected] 无论被多少项目引用,在磁盘上永远只存在一份物理数据。

在非扁平化的 node_modules 布局方面,pnpm 率先采用严格隔离策略:所有依赖被安装在 node_modules/.pnpm/ 下的一个由包名和版本号构成的虚拟存储结构中,项目真正使用的依赖通过符号链接从 .pnpm/ 目录暴露到顶层 node_modules/。这种设计杜绝了"幽灵依赖"(代码中引用了未在 package.json 中声明的包)问题,因为它使未声明的依赖在文件系统中根本不可访问。

在 monorepo 场景下,pnpm 内建工作区(Workspaces)支持,配合 pnpm-workspace.yaml 可以统一管理多个子包的依赖解析、版本协调和交叉引用。pnpm v10 引入的 Catalogs 机制进一步让 monorepo 可以在单一位置集中声明依赖版本,避免各子包之间的版本漂移。

性能层面,pnpm 官方宣称安装速度比 npm 快约 3 倍,且在冷启动和热启动场景下均保持显著优势。2026 年 4 月发布的 v11.0 将存储索引从数百万个 JSON 文件迁移到单一 SQLite 数据库,大幅提升了索引访问的 I/O 效率,特别是在包含数十万个包的仓库中,文件系统压力得到根本性缓解。

基本信息

属性说明
官方网站https://pnpm.io/
GitHub 仓库https://github.com/pnpm/pnpm
开源许可证MIT License
当前最新版本v11.10.0(2026 年 7 月)
主版本演进v10.0(2025-01-07)→ v11.0(2026-04-28)→ v11.10.0
创始人Zoltan Kochan
当前维护方pnpm 社区(Community-driven)
实现语言TypeScript(v11 起为纯 ESM)
运行要求Node.js ≥ 22.0.0(v11+)
Registry 兼容npm registry(registry.npmjs.org 及私有兼容服务)

快速上手

安装

pnpm 提供三种主流安装方式,推荐根据环境选择。

方式一:独立安装脚本(推荐用于系统级安装)

# Linux / macOS
curl -fsSL https://get.pnpm.io/install.sh | sh -
 
# Windows (PowerShell)
Invoke-WebRequest https://get.pnpm.io/install.ps1 -UseBasicParsing | Invoke-Expression
 
# 安装完成后将 pnpm 加入 PATH(脚本会提示对应的 shell 配置命令)

方式二:通过 npm 全局安装

npm install -g pnpm

方式三:通过 Node.js Corepack(推荐用于项目级锁定)

Node.js 自 v16.9.0 起内建 Corepack,可自动管理 pnpm 版本,无需全局安装。

# 启用 Corepack
corepack enable
 
# 在 package.json 中声明所需 pnpm 版本
# {
#   "packageManager": "[email protected]"
# }
 
# 之后在项目目录中执行任何 pnpm 命令,Corepack 会自动使用指定版本
pnpm install

基础配置

pnpm v11 起,项目配置由 .npmrc 迁移至 pnpm-workspace.yaml

pnpm-workspace.yaml 是 v11 的核心配置文件,承载工作区声明、依赖版本目录(Catalogs)、构建安全策略等配置:

# pnpm-workspace.yaml
# 声明 monorepo 中的工作区包路径
packages:
  - 'apps/*'
  - 'packages/*'
 
# Catalogs:集中声明依赖版本,各子包通过 catalog: 协议引用
catalog:
  react: ^19.1.0
  typescript: ^5.8.0
  '@types/node': ^22.15.0
 
# 构建安全:仅允许明确列出的包运行 postinstall 等生命周期脚本
onlyBuiltDependencies:
  - esbuild
  - sharp
 
# 构建白名单(v11 合并了此前的多项构建配置)
allowBuilds:
  esbuild: true
  sharp: true
 
# 供应链保护(v11 默认开启)
minimumReleaseAge: 1440  # 单位:分钟,1440 = 1 天
blockExoticSubdeps: true

.npmrc 在 v11 中仅用于与 registry 鉴权相关的配置:

# .npmrc(v11 中仅保留鉴权与 registry 地址配置)
registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=${NPM_TOKEN}
 
# 私有 registry 示例
@myorg:registry=https://npm.pkg.github.com/
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}

最小示例

# 初始化项目
mkdir my-app && cd my-app
pnpm init
 
# 安装依赖
pnpm add lodash
pnpm add -D typescript @types/node
 
# 安装指定版本
pnpm add [email protected]
 
# 移除依赖
pnpm remove lodash
 
# 执行 package.json 中的脚本
pnpm run build   # 或简写为 pnpm build(pnpm 支持脚本名称直接调用)

执行 pnpm install 后,生成的目录结构如下:

my-app/
├── node_modules/
│   ├── .pnpm/              # 虚拟存储:所有依赖的真实安装位置
│   │   ├── [email protected]/
│   │   │   └── node_modules/lodash/   # 硬链接指向全局 Store
│   │   └── [email protected]/
│   │       └── node_modules/typescript/
│   ├── lodash -> .pnpm/[email protected]/node_modules/lodash   # 符号链接
│   └── typescript -> .pnpm/[email protected]/node_modules/typescript
├── pnpm-lock.yaml          # 锁定文件
├── package.json
└── pnpm-workspace.yaml     # 工作区配置(v11+)

核心概念与架构

内容寻址存储(Content-Addressable Store)

pnpm 在磁盘上维护一个全局的内容寻址仓库(Store),默认位于:

  • Linux / macOS:~/.local/share/pnpm/store/
  • Windows:%LOCALAPPDATA%\pnpm\store\

每个从 registry 下载的包文件(.tgz 解包后的实际文件)以 SHA-256 内容哈希命名后存入 Store。相同内容的文件无论来源如何,只会存储一份——这是典型的内容寻址设计,与 Git 的对象存储原理类似。

Store v11:SQLite 索引

在 v10 及更早版本中,Store 使用一个目录树结构存储索引,每个包版本对应一个 JSON 文件。当 Store 中包含数十万个包版本时,文件系统的元数据压力显著。

v11.0 引入 Store v11,将所有索引数据迁移至单一 SQLite 数据库文件(store.db)。这一变更带来以下收益:

  • I/O 效率:SQLite 的 B-tree 索引结构使查找操作从 O(n) 文件系统遍历降低到 O(log n) 数据库查询
  • 文件系统压力:消除了数万乃至数十万个小 JSON 文件,大幅降低 inode 消耗
  • 原子性:SQLite 的写事务保证索引更新不会出现半写状态
  • 并发安全:多进程同时访问 Store 时,SQLite 的锁机制提供了可靠的并发控制

Store 中的文件通过硬链接映射到 node_modules/.pnpm/ 下对应的包目录。硬链接是操作系统级别的文件系统特性,它使多个目录路径指向磁盘上同一份数据节点(inode),而不产生数据拷贝。

这意味着:

  • 项目 A 和项目 B 都依赖 [email protected],磁盘上只有一份物理文件
  • 删除项目 A 不会影响项目 B 对同一文件的访问(硬链接计数减一,但不删除数据)
  • 安装速度接近文件系统的元数据操作速度,而非数据拷贝速度

符号链接(Symbolic Links)与 node_modules 布局

pnpm 使用符号链接构建项目实际可访问的 node_modules/ 目录:

node_modules/
├── .pnpm/                          # 虚拟存储层
│   ├── [email protected]/
│   │   └── node_modules/
│   │       ├── pkgA/               # 硬链接 → Store
│   │       └── pkgB/               # pkgA 的依赖,硬链接 → Store
│   └── [email protected]/
│       └── node_modules/
│           └── pkgB/               # 硬链接 → Store
├── pkgA -> .pnpm/[email protected]/node_modules/pkgA   # 符号链接
└── pkgB -> .pnpm/[email protected]/node_modules/pkgB   # 符号链接

这种布局的核心价值在于:

  1. 幽灵依赖消除:未在 package.json 中声明的包,其符号链接不会存在于顶层 node_modules/,访问时直接抛出 MODULE_NOT_FOUND
  2. 版本隔离:同一包的不同版本共存于 .pnpm/ 下,互不干扰
  3. 依赖树可预测性node_modules/ 的结构严格由 package.json 声明决定,不受安装顺序影响

pnpm 与 npm / Yarn 的 node_modules 对比

特性npm(v3+)Yarn(Classic)pnpm
依赖布局扁平化(hoisting)扁平化(hoisting)严格隔离(非扁平)
幽灵依赖可能发生可能发生杜绝
磁盘复用否(拷贝)部分(全局缓存)是(硬链接)
循环依赖处理依赖 hoisting 顺序依赖 hoisting 顺序天然支持(符号链接)

核心特性

1. 极致的磁盘效率

得益于内容寻址存储与硬链接机制,pnpm 在多项目环境下的磁盘占用显著低于 npm 和 Yarn。实测数据表明,在一个包含 50 个 Node.js 项目的开发机器上,pnpm 的磁盘占用可比 npm 减少 40%~60%。在 monorepo 中,这一优势更加明显,因为多个子包共享依赖时只需一份物理数据。

2. 安装速度

pnpm 的安装速度在业界持续领先,官方基准测试显示比 npm 快约 3 倍。速度优势来源于三个方面:

  • 硬链接替代数据拷贝,减少磁盘 I/O
  • 并行安装多个包,充分利用 I/O 并发能力
  • Store v11 的 SQLite 索引消除文件系统瓶颈

3. 严格 node_modules 隔离

pnpm 默认采用非扁平化的 node_modules 布局,从结构上杜绝幽灵依赖问题。这一设计在大型项目中尤为重要——它能捕获那些依赖了"恰好被其他包安装进来"的未声明依赖的隐性 bug。

对于确实需要访问未声明依赖的场景(通常是遗留代码迁移期),pnpm 提供 public-hoist-pattern 配置进行有限度的 hoisting:

# .npmrc 或 pnpm-workspace.yaml(v11 推荐在 pnpm-workspace.yaml 中配置)
public-hoist-pattern[]=*types*

4. 原生工作区(Workspaces)支持

pnpm 内建 monorepo 工作区支持,通过 pnpm-workspace.yaml 声明包路径:

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

工作区特性包括:

  • pnpm --filter <package> <command>:针对特定子包执行命令
  • pnpm -r <command>:递归对所有子包执行命令
  • workspace:* 协议:子包间引用使用本地源码而非 registry 版本
  • 跨子包依赖提升:共享依赖自动协调

5. Catalogs(版本目录,v10.15+)

Catalogs 允许在 monorepo 的根目录集中声明依赖版本,各子包通过 catalog: 协议引用:

# pnpm-workspace.yaml(根目录)
catalog:
  react: ^19.1.0
  next: ^16.0.0
  typescript: ^5.8.0
 
catalog:
  internal:  # 命名 catalog
    '@myorg/ui': workspace:*
    '@myorg/utils': workspace:*
// apps/web/package.json
{
  "dependencies": {
    "react": "catalog:",
    "next": "catalog:",
    "@myorg/ui": "catalog:internal"
  }
}

这一机制使版本升级只需修改根目录一处,所有子包自动同步。

6. 供应链安全

pnpm 在供应链安全方面的投入是其区别于其他包管理器的重要特征,v10 和 v11 两个大版本均以此为核心主题。

onlyBuiltDependencies(v10.0+):默认阻止所有包的 postinstallpreinstall 等生命周期脚本,仅允许白名单中的包执行。这有效防止了恶意包通过安装脚本注入代码:

# pnpm-workspace.yaml
onlyBuiltDependencies:
  - esbuild
  - sharp
  - core-js  # 明确允许的包

minimumReleaseAge(v11.0 默认开启,1440 分钟):拒绝安装发布不足 1 天的包版本。这为应对恶意包抢注(typosquatting、dependency confusion 等攻击)提供了时间窗口缓冲——一旦某个包被发现存在安全问题,1 天的延迟可以让社区在恶意版本进入生产环境之前发出警报。

blockExoticSubdeps(v11.0 默认开启):阻止包含可疑特征的间接依赖,进一步收窄供应链攻击面。

strictDepBuilds(v11.0 默认开启):对所有依赖的构建行为实施严格审查。

pnpm audit --fix=update(v11.0 新增):在审计漏洞的同时,自动将受影响的依赖更新到已修复版本。

pnpm sbom(v11.0 新增):生成软件物料清单(SBOM),用于合规审查和漏洞追踪。

7. 原生发布流程(Native Publish,v11.0+)

v11 之前,pnpm publish 在底层委托给 npm CLI 完成实际的发布操作。v11 重构为完全原生实现,不再依赖 npm CLI。这消除了对 npm 版本兼容性的依赖,并使发布行为更加可预测和可控。

8. 隔离全局安装(Isolated Global Installs,v11.0+)

pnpm add -g <package> 在 v11 中会为每个全局包创建独立的隔离目录,避免全局包之间的依赖冲突。这与传统的"所有全局包共享一个 node_modules/"的模式形成对比,提高了全局工具的稳定性和可维护性。

9. pnpm ci:CI 优化命令(v11.0+)

专为持续集成环境设计的安装命令,行为特点:

  • 严格遵循 pnpm-lock.yaml,存在差异时报错退出
  • 跳过不必要的缓存检查,优化 CI 执行速度
  • 适合替代 pnpm install --frozen-lockfile 的用法

10. pnpm clean:工作区清理(v11.0+)

一键清理所有工作区包中的构建产物、node_modules 和临时文件,适合在 CI 环境或本地开发中重置工作区状态。

11. Node.js 运行时安装(v10.21+)

pnpm 支持为依赖自动安装指定版本的 Node.js 运行时,解决部分原生插件需要特定 Node.js 版本才能编译的问题,避免开发者手动切换 Node.js 版本。

12. Trust 策略配置(v10.21+)

提供显式的信任策略机制,允许开发者逐一审查并授权特定包的运行时行为,进一步收紧供应链控制粒度。

生态图

Monorepo 工具链集成

pnpm 与主流 monorepo 工具形成完整的工程化生态:

工具与 pnpm 的关系说明
Turborepo深度集成pnpm Workspaces 的增量构建与任务编排;Vercel 维护
Nx兼容支持通过 @nx/js 插件支持 pnpm 作为包管理器
Rush可选后端Rush 支持 pnpm 作为底层包管理器(rush init 时选择)
Lerna兼容支持Lerna v5+ 支持 pnpm Workspaces
Changesets原生支持@changesets/cli 直接读取 pnpm 工作区结构

Corepack 集成

Node.js Corepack(自 v16.9.0)将 pnpm 作为官方推荐的包管理器之一进行内建支持。通过在 package.json 中声明 packageManager 字段,团队成员无需手动安装 pnpm,Corepack 会自动下载并使用指定版本,消除版本不一致问题:

{
  "packageManager": "[email protected]"
}

编辑器与 IDE 支持

  • VS Code:通过 pnpm 扩展或直接使用终端集成;node_modules 结构在 VS Code 的 TypeScript 语言服务中正常工作
  • WebStorm / IntelliJ:内建 pnpm 支持,可在设置中选择 pnpm 作为包管理器
  • Neovim:通过 LSP 和终端插件完整支持

插件生态

pnpm 通过 pnpmfile.cjs(项目级)支持安装过程钩子,可在依赖解析完成后、写入 node_modules 前修改依赖树。这在需要批量替换依赖版本或注入内部包的场景中非常有用:

// pnpmfile.cjs
function readPackage(pkg, context) {
  if (pkg.name === 'some-legacy-pkg') {
    pkg.dependencies['vulnerable-dep'] = '^2.0.0'  // 强制替换版本
  }
  return pkg
}
 
module.exports = { hooks: { readPackage } }

适用场景

大型 Monorepo

pnpm 是大型 monorepo 事实上的最佳选择。磁盘效率在多子包共享依赖时发挥到极致,工作区原语与 workspace:* 协议使子包间引用清晰且高效。配合 Turborepo 或 Nx,可以构建出高度可扩展的 monorepo 工程体系。

典型适用:

  • 多个前端应用(Web、移动端、桌面端)共享组件库和工具包
  • UI 库 + 设计系统 + 文档站点的多包发布
  • 微前端架构下的独立子应用管理

CI/CD 流水线

pnpm 在 CI 环境中的优势体现在三方面:

  1. 安装速度:硬链接机制结合 Store 缓存,使 pnpm ci 在热缓存场景下接近瞬时完成
  2. 可重现性:严格 node_modules 布局 + pnpm-lock.yaml 保证跨环境一致性
  3. CI 平台支持:GitHub Actions、GitLab CI、CircleCI 均有官方或社区提供的缓存配置示例
# GitHub Actions 示例
- uses: pnpm/action-setup@v4
  with:
    version: 11.10.0
- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: 'pnpm'

磁盘空间受限环境

在 CI Runner 磁盘空间有限、开发机器 SSD 容量紧张、或 CI 缓存配额受限的场景下,pnpm 的磁盘效率优势最为直接。一个典型的前端项目 node_modules/ 可达数百 MB,pnpm 通过硬链接将实际新增磁盘占用降低至仅元数据级别。

安全敏感项目

pnpm v10 和 v11 的供应链安全特性使其成为对安全合规有严格要求的项目的首选:

  • onlyBuiltDependencies 防止安装脚本攻击
  • minimumReleaseAge 提供新包发布的观察期
  • pnpm sbom 支持软件物料清单生成
  • pnpm audit --fix=update 自动修复已知漏洞

需要严格依赖隔离的项目

当项目希望彻底消除"依赖侥幸"(即代码引用了未在 package.json 中声明但恰好可用的包)时,pnpm 的严格 node_modules 布局是唯一不需要额外配置就能提供此保证的主流包管理器。

开发与工程化

开发工作流

日常开发

# 安装所有依赖
pnpm install
 
# 添加新依赖到根目录
pnpm add -w <package>
 
# 添加依赖到特定工作区子包
pnpm --filter @myorg/ui add <package>
 
# 运行所有子包的构建脚本
pnpm -r build
 
# 仅运行受变更影响的子包构建(配合 Turborepo 使用效果更佳)
pnpm --filter ...@myorg/app-web build

依赖更新

# 查看所有过期依赖
pnpm outdated
 
# 交互式更新依赖
pnpm update --interactive
 
# 更新特定依赖到最新版本
pnpm update <package> --latest

CI 优化策略

缓存配置

pnpm 的 Store 天然适合 CI 缓存。GitHub Actions 中推荐使用官方 action:

- uses: pnpm/action-setup@v4
- uses: actions/setup-node@v4
  with:
    cache: 'pnpm'  # 自动缓存 Store 目录

对于 GitLab CI,手动配置缓存路径:

cache:
  key:
    files:
      - pnpm-lock.yaml
  paths:
    - .local/share/pnpm/store/
    - node_modules/

增量构建

配合 Turborepo,pnpm 支持跨 CI 运行的增量构建缓存,未变更的子包可以直接从缓存恢复构建产物,跳过实际构建过程。

工作区管理最佳实践

目录结构

my-monorepo/
├── apps/
│   ├── web/          # 前端应用
│   ├── api/          # 后端服务
│   └── docs/         # 文档站
├── packages/
│   ├── ui/           # 共享 UI 组件
│   ├── utils/        # 工具函数
│   └── types/        # 共享类型定义
├── pnpm-workspace.yaml
├── pnpm-lock.yaml
├── package.json
└── turbo.json        # Turborepo 配置

子包版本引用规范

// apps/web/package.json
{
  "dependencies": {
    "@myorg/ui": "workspace:*",        // 始终使用本地源码
    "react": "catalog:",               // 使用 Catalog 声明的版本
    "lodash": "^4.17.21"               // 直接指定版本(不推荐在 monorepo 中使用)
  }
}

发布流程

发布单个包

# 在目标包目录下
cd packages/ui
pnpm version patch    # 版本号递增
pnpm publish          # 发布到 registry(v11 使用原生发布流程)

批量发布(配合 Changesets)

# 记录变更
pnpm changeset
 
# 根据变更记录批量更新版本号并发布
pnpm changeset version
pnpm -r publish --access public

性能与安全

性能基准

pnpm 的安装速度优势在多个独立基准测试中得到验证:

场景npmYarn ClassicYarn Berrypnpm
干净安装(无缓存)基准~1.2x~0.9x~2-3x
热缓存安装基准~1.5x~1.1x~3x+
添加单个包基准~1.3x~1.0x~2x

注:具体数字因项目规模、网络条件和硬件配置而异,以上为典型相对表现。

Store v11 的性能提升

Store v11 的 SQLite 索引相比 v10 的 JSON 文件索引,在以下方面有显著提升:

  • 索引访问延迟:从文件系统的毫秒级目录遍历降低到 SQLite 的微秒级 B-tree 查询
  • 大规模仓库:在 Store 包含超过 10 万个包版本时,索引性能提升尤为显著
  • 磁盘 I/O:消除了文件系统的元数据操作开销,降低了对 SSD 寿命的消耗

供应链安全体系

pnpm 在 v10 和 v11 中构建了业界领先的供应链安全体系:

特性版本默认状态说明
onlyBuiltDependenciesv10.0严格模式默认阻止所有包的安装脚本
minimumReleaseAgev11.01440 分钟拒绝安装发布不足 1 天的包
blockExoticSubdepsv11.0true阻止可疑间接依赖
strictDepBuildsv11.0true严格审查依赖构建行为
pnpm audit --fix=updatev11.0审计并自动修复漏洞
pnpm sbomv11.0生成软件物料清单
Trust 策略v10.21显式信任授权机制

pnpm audit 与漏洞管理

# 审计所有依赖的已知漏洞
pnpm audit
 
# 审计并自动更新到已修复版本
pnpm audit --fix=update
 
# 生成 SBOM(Software Bill of Materials)
pnpm sbom --format cyclonedx > sbom.json

技术对比

综合对比表

维度npm(v10+)Yarn Berry(v4+)Bun(v1.x)pnpm(v11+)
安装速度中等较快最快(原生 Zig)快(硬链接优化)
磁盘效率差(完整拷贝)中(全局缓存)极佳(硬链接 Store)
node_modules 隔离扁平化,有幽灵依赖可配置 PnP / 扁平扁平化严格隔离,无幽灵依赖
Monorepo 支持Workspaces(v7+)WorkspacesWorkspaces(有限)原生 Workspaces + Catalogs
供应链安全基础 audit基础 audit业界领先(多层防护)
Lock 文件格式YAML(v9+)YAML二进制YAML
Offline 支持有限强(全局缓存)强(Store + 硬链接)
Node.js 要求≥ 18≥ 18Bun 运行时≥ 22(v11+)
包管理器本身格式CJS/ESMESM原生二进制纯 ESM(v11+)
发布机制原生原生原生原生(v11+)
社区生态最广泛较大快速增长较大,高质量

选型建议

  • 首选 pnpm 的场景:Monorepo、磁盘空间敏感、安全合规要求高、需要严格依赖隔离
  • 首选 Bun 的场景:追求极致安装速度、已使用 Bun 运行时、对供应链安全要求不高
  • 首选 npm 的场景:简单项目、无需额外学习成本、生态兼容性优先
  • 首选 Yarn Berry 的场景:已有 Yarn 历史积累、使用 PnP 模式、需要 Zero-installs

最佳实践

工作区配置建议

使用 Catalogs 集中管理版本

在 monorepo 中,始终通过 pnpm-workspace.yaml 的 Catalogs 功能集中声明共享依赖版本,避免子包间版本漂移:

# pnpm-workspace.yaml
catalog:
  # 框架
  react: ^19.1.0
  react-dom: ^19.1.0
  next: ^16.0.0
 
  # 工具链
  typescript: ^5.8.0
  vite: ^7.0.0
  vitest: ^3.2.0
 
  # 类型定义
  '@types/node': ^22.15.0
  '@types/react': ^19.1.0

使用 workspace:* 管理内部包引用

子包间引用始终使用 workspace:* 协议,确保本地开发时直接使用源码而非 registry 版本:

{
  "dependencies": {
    "@myorg/ui": "workspace:*",
    "@myorg/utils": "workspace:*"
  }
}

构建安全配置

明确声明 onlyBuiltDependencies 白名单

不要使用 * 通配符绕过构建脚本限制,而应逐一审查并明确列出确实需要执行安装脚本的包:

# pnpm-workspace.yaml
onlyBuiltDependencies:
  - esbuild        # 需要下载平台特定二进制
  - sharp          # 需要编译原生插件
  - core-js        # 需要执行 polyfill 注入(评估后决定)

保持 minimumReleaseAge 开启

默认的 1440 分钟(1 天)为合理起点,对于安全要求更高的项目可以增加到 4320 分钟(3 天):

minimumReleaseAge: 4320  # 3 天

从 v10 迁移到 v11

第一步:确认 Node.js 版本

node -v  # 需要 >= 22.0.0

第二步:迁移配置文件

将原先在 .npmrc 中的项目配置迁移到 pnpm-workspace.yaml.npmrc 仅保留 registry 鉴权相关配置:

# 迁移前(.npmrc 中包含项目配置)
cat .npmrc
# registry=https://registry.npmjs.org/
# strict-peer-dependencies=true
# auto-install-peers=true
 
# 迁移后(.npmrc 仅保留鉴权)
cat .npmrc
# registry=https://registry.npmjs.org/
# //registry.npmjs.org/:_authToken=${NPM_TOKEN}
 
# 迁移后(pnpm-workspace.yaml 包含项目配置)
cat pnpm-workspace.yaml
# packages:
#   - 'apps/*'
#   - 'packages/*'
# strictPeerDependencies: true
# autoInstallPeers: true

第三步:审查构建白名单

v11 中多项构建配置合并为 allowBuilds,检查并更新配置。

第四步:删除并重新安装

# 删除旧的 node_modules 和 Store v10 索引
rm -rf node_modules
pnpm store prune
 
# 重新安装(自动初始化 Store v11 SQLite 索引)
pnpm install

CI 环境推荐配置

# 完整 CI 示例(GitHub Actions + Turborepo + pnpm)
name: CI
on:
  push:
    branches: [main]
  pull_request:
 
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
 
      - uses: pnpm/action-setup@v4
        with:
          version: 11.10.0
 
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'pnpm'
 
      - run: pnpm ci                    # 严格模式安装
      - run: pnpm audit                 # 安全审计
      - run: pnpm lint                  # 代码检查
      - run: pnpm typecheck             # 类型检查
      - run: pnpm test --ci             # 单元测试
      - run: pnpm build                 # 构建(由 Turborepo 编排)

技术局限与边界

纯 ESM 要求(v11+)

pnpm v11 本身为纯 ESM 实现。这意味着:

  • 不能通过 require() 直接加载 pnpm 的内部模块(针对 pnpm 插件开发场景)
  • pnpmfile.cjs 在 v11 中的行为需要验证,部分旧插件可能需要适配

Node.js 版本要求

v11+ 强制要求 Node.js ≥ 22.0.0。对于仍在使用 Node.js 20 LTS 的项目,需要:

  • 在升级 pnpm 之前先升级 Node.js 版本
  • 或使用 Corepack 锁定 pnpm v10 版本,待 Node.js 升级完成后再迁移

配置文件迁移成本

从 v10 迁移到 v11 需要将项目配置从 .npmrc 迁移至 pnpm-workspace.yaml。对于大型 monorepo,这一过程需要逐一审查和迁移配置项,存在一定的人工成本。

严格模式带来的兼容性挑战

pnpm 的严格 node_modules 隔离会暴露那些依赖幽灵依赖的遗留代码。对于从 npm 或 Yarn 迁移过来的大型项目,这可能导致大量 MODULE_NOT_FOUND 错误,需要逐一补充缺失的依赖声明。

缓解策略:

# pnpm-workspace.yaml(迁移过渡期)
# 临时允许部分 hoisting,逐步修复
public-hoist-pattern:
  - '*types*'           # 类型定义包通常被隐式引用
  - '*eslint*'          # ESLint 插件通常不显式声明

Store v11 迁移

从 v10 升级到 v11 时,旧版 Store 的 JSON 文件索引不会自动迁移到 SQLite 格式。pnpm 会在首次运行时自动创建新的 SQLite 索引,但旧索引文件需要手动清理:

pnpm store prune  # 清理无效条目
# 旧 JSON 索引文件可手动删除(参考官方迁移指南)

与私有 Registry 的兼容性

pnpm 与标准 npm registry 协议完全兼容,但部分私有 registry 的非标准扩展(如特定平台的鉴权机制)可能存在兼容性问题,需在升级前验证。

学习资源

官方资源

社区资源

推荐学习路径

  1. 入门:阅读官方文档的 "Motivation" 章节,理解内容寻址存储的设计原理
  2. 实践:在新项目中使用 pnpm init 并开始日常使用,熟悉命令和工作流
  3. 进阶:在 monorepo 中配置 Workspaces、Catalogs 和 Turborepo 集成
  4. 深入:阅读 Store v11 的 SQLite 索引设计和供应链安全机制的技术文档
  5. 贡献:参与 GitHub Issues 和 Discussions,帮助完善文档或修复问题

2026 年现状

版本活跃演进

pnpm v11 自 2026 年 4 月发布以来保持活跃迭代,截至 2026 年 7 月已更新至 v11.10.0。Store v11 的 SQLite 索引在实际应用中持续获得性能优化,供应链安全特性也在社区反馈下不断完善。

业界推荐地位

pnpm 在 2025~2026 年的多项前端工具链调研中被推荐为综合最佳包管理器。其优势在以下维度得到广泛认可:

  • 磁盘效率:在多项目、monorepo 开发环境中的磁盘节省效果显著,成为团队选择 pnpm 的首要动因
  • 安全性:v10 和 v11 的供应链安全特性使 pnpm 成为安全敏感场景的首选,也是 npm 生态中安全能力最全面的包管理器
  • 速度:安装速度持续领先 npm,与 Bun 相比在具体场景下各有优势
  • Monorepo 支持:原生 Workspaces + Catalogs + 严格隔离的组合,使 pnpm 在 monorepo 场景几乎没有竞品

生态整合

pnpm 与 Turborepo(Vercel 维护)的深度整合使其成为 Next.js / React 生态 monorepo 的默认推荐方案。Node.js Corepack 的持续推广也降低了团队采用 pnpm 的门槛。

供应链安全领导地位

在 2024~2025 年多起 npm 供应链攻击事件(如 ua-parser-js 被入侵、多个 typosquatting 攻击)之后,pnpm 的 minimumReleaseAgeonlyBuiltDependenciesblockExoticSubdeps 等特性受到更多关注。2026 年,pnpm 被多个安全合规框架(包括企业级软件供应链安全标准)列为推荐工具。

展望

pnpm 社区正在探索:

  • Store v11 SQLite 索引的进一步性能优化(包括增量同步和远程 Store 支持)
  • 更细粒度的供应链安全策略(如基于包来源的访问控制)
  • 与 Node.js 核心团队的更紧密协作,推动包管理器标准的统一