
pnpm 12 发布之后技术社区讨论最多的话题就是 Rust 重写到底带来多少性能提升。对于还在用 npm 或者旧版 pnpm 的团队来说最关心的往往不是发布公告里那些架构术语而是升级之后安装依赖是不是真的更快、迁移成本高不高、老项目会不会报错。这篇文章会从安装升级、镜像配置、项目初始化、性能对比、常见报错五个方面把 pnpm 12 这套东西讲透。先给结论pnpm 这套工具的核心能力一直是“快”和“省”。它通过全局 store 加硬链接的方式避免同一个版本的依赖在多个项目里重复下载同时用内容寻址存储保证 node_modules 结构可以被严格校验。Rust 重写则主要针对依赖解析、tarball 解压、文件搬运这些性能敏感环节目标是降低安装时的 CPU 消耗提高并发处理的稳定性。具体快多少不同项目、不同磁盘、不同 Node 版本差异很大不建议直接背官方跑分更好的做法是拿自己项目做一次安装耗时和磁盘占用对比。如果你正在做前端工程化、Monorepo 改造、CI 构建优化或者只是想把手上的包管理器换成更省磁盘空间的方案这篇文章可以直接收藏。下面按“部署-验证-排错”的顺序展开。1. 核心能力速览先看 pnpm 12 的整体情况。下面的表格是围绕实际使用整理的适合作为快速判断依据。能力项说明项目类型Node.js 包管理器可替代 npm / yarn当前话题pnpm 12 正式发布性能敏感链路 Rust 化Node.js 版本要求需确认当前 pnpm 版本的 engines 字段低版本 Node 会报版本不满足错误安装方式corepack、npm 全局安装、独立脚本、二进制包核心机制全局 store 硬链接内容寻址存储主要功能依赖安装、Scripts 运行、Monorepo workspace、选择性依赖、多镜像源批量任务支持 shell 脚本批量执行、CI 流水线集成API 接口不提供独立的 HTTP 服务核心是 CLI 接口典型场景Web 项目依赖管理、Monorepo、CI 构建、前端部署发布边界说明依赖项目存在 postinstall 脚本时需要 approve-builds 安全确认这张表里没有写具体的“安装提速 3 倍”“磁盘占用减少 50%”之类的数字原因是这类数据必须结合具体依赖树、磁盘类型、Node 版本和缓存状态才能测出来。后面的章节会给出一套可以自己复现的验证流程用你自己项目的实际结果来判断。2. pnpm 12 与 Rust 重写性能提升到底在哪个环节2.1 为什么 pnpm 要引入 Rustpnpm 早期核心逻辑大量使用 TypeScript 编写。TypeScript 的优势是生态成熟、维护方便但依赖解析、tarball 解压、大量小文件处理这类重负载场景JavaScript 的运行时开销确实更高。Rust 重写并不是把整个 pnpm 一次性推翻重建而是把性能敏感模块逐步替换成 Rust 实现比如依赖图的解析、压缩包解压、文件复制和硬链接创建等环节。从工程角度看这种“渐进式 Rust 化”对用户更友好。它不需要你改变命令习惯也不需要为 pnpm 额外安装 Rust 工具链。pnpm 最终发布给用户的是一个编译好的可执行文件普通开发者感知不到底层语言变化能感知到的只是安装依赖时的 CPU 占用和等待时间。2.2 性能收益的边界在哪里这里需要把话说清楚Rust 重写能优化的部分主要集中在“安装依赖”这条链路上也就是解析 lockfile、下载 tarball、解压、写入 node_modules、创建硬链接。它不会让业务代码运行变快也不会让pnpm run dev之后的浏览器刷新速度有变化。搞清楚这个边界才不会被标题党误导。同时性能提升不是线性的。一个项目如果只有十几个依赖冷安装也就几秒钟升级到 pnpm 12 后体感差异可能不明显。但如果是几百个依赖的 Monorepo或者需要频繁清缓存重建 node_modules 的 CI 环境Rust 重写带来的稳定性收益就会放大。还有一个容易被忽略的点pnpm 的全局 store 如果已经存在大量缓存命中安装耗时本身就会很低这时候再用旧版本和新版本对比差距也不会特别大。2.3 可观测的验证手段判断 Rust 重写有没有效果最直接的方法是控制变量做安装对比。具体操作后面第 6 章会详细写这里先提一个思路准备同一个项目、同一份 lockfile、同一台机器分别用旧版 pnpm、新版 pnpm、npm 执行冷安装记录 wall time、CPU 时间、node_modules 磁盘占用和安装完成后的 store 大小。用真实数据做结论比引用任何来自新闻稿的数字都可靠。3. 适用场景与使用边界3.1 适合什么场景第一类是前端项目依赖管理。pnpm 对 npm 和 yarn 的 lockfile 迁移路径比较成熟普通 Web 项目升级成本不高。第二类是 Monorepo。pnpm 原生支持 workspace通过pnpm-workspace.yaml管理多个子包配合pnpm --filter可以只安装或发布指定包非常契合多包仓库。第三类是 CI 构建优化。pnpm 支持--frozen-lockfile在 CI 里可以避免 lockfile 被意外修改同时 store 缓存可以跨构建复用减少重复下载。第四类是磁盘空间敏感的开发机。pnpm 的硬链接机制让多个项目共享同一份依赖文件依赖越多、项目越多省下的磁盘空间越明显。3.2 不适合什么场景和合规边界如果团队对 postinstall 脚本没有审核流程升级 pnpm 10 以上版本后会有一定的学习成本。新版 pnpm 默认拦截依赖的自动构建脚本必须主动执行pnpm approve-builds去允许指定依赖运行脚本。这个设计是为了安全但对习惯了全自动安装的开发者来说第一次遇到会有“安装不完整”的错觉。此外涉及私有 npm 镜像、企业内网源、离线环境时需要提前把 registry 配置和 store 目录规划好否则迁移到 Rust 版本后可能出现下载失败或鉴权问题。最后强调合规边界pnpm 本身是开源工具但项目里的依赖来自第三方。安装依赖前要关注包的许可证、来源和 postinstall 脚本行为尤其是从非官方镜像下载包时不要轻易放行来自陌生维护者的安装脚本。4. 环境准备与前置条件4.1 检查 Node.js 版本pnpm 12 对 Node.js 版本有明确要求。如果你本机 Node 版本过旧执行 pnpm 命令时可能会看到类似下面的报错error: this version of pnpm requires at least node.js v22.13 the current ver遇到这种提示不需要去翻文档优先升级 Node.js。推荐使用 Node 版本管理工具避免直接覆盖系统级 Node 环境。Windows 用户可以用 nvm-windowsnvm install 22 nvm use 22 nvm listmacOS 和 Linux 用户可以用 nvm 或 mise# nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash nvm install 22 nvm use 22 # mise mise install node22 mise use node22值得多说一句mise 这类工具现在也很常用它不仅能管 Node.js还能管 pnpm 本身热词里提到的“mise 安装 pnpm”就是这种用法。等 Node.js 版本确认没问题后再安装 pnpm 会少很多坑。4.2 安装 pnpm 12安装 pnpm 的方式有很多这里给出三种常用的。第一种用 corepack 安装。corepack 是 Node.js 自带的工具适合需要自动切换包管理器版本的场景corepack enable corepack prepare pnpmlatest --activate第二种用 npm 全局安装。这是最直接的方式大多数环境下都能用npm install -g pnpm第三种在不确定全局环境是否干净的时候可以用 npx 临时运行最新版本先验证命令是否正常npx pnpmlatest --version安装完成后验证一下pnpm --version如果你看到 pnpm 版本号正常输出说明安装成功。如果在 Windows 上提示“pnpm 不是内部或外部命令”先不要重新安装优先检查 PATH 环境变量是否包含 npm 全局目录。4.3 设置国内镜像安装慢、下载失败、install 一直卡住大多数时候是 registry 网络问题。pnpm 12 的镜像配置方式和 npm 很像pnpm config set registry https://registry.npmmirror.com pnpm config get registry如果你只想在当前项目生效可以在项目根目录创建.npmrcregistryhttps://registry.npmmirror.com某些企业环境会使用私有镜像配置方法同理把 registry 地址替换成内网地址即可。需要注意镜像源只影响依赖包的下载不影响 lockfile 的生成逻辑所以切换镜像后不需要重写 lockfile。5. 快速上手初始化项目与安装依赖5.1 初始化项目新项目可以直接用 pnpm 生成 package.jsonpnpm init如果你是在已有项目里迁移只需要确认已经有 package.json 和 lockfile。pnpm 会读取已有 lockfile 并生成自己的pnpm-lock.yaml。第一次切换包管理器时建议提交前先运行一遍完整安装确保没有遗漏。5.2 安装依赖在项目根目录执行pnpm install如果需要在 CI 或部署环境严格复现依赖使用pnpm install --frozen-lockfile这个命令会检查 lockfile 是否与 package.json 同步一旦不一致就会直接报错不会偷偷修改 lockfile。这是保证生产环境可重复构建的关键。安装完成后node_modules 里会出现一个.pnpm目录这是 pnpm 的内容寻址存储入口。你还会看到pnpm-lock.yaml务必提交到 Git不要忽略。5.3 观察硬链接存储pnpm 的省磁盘思路和 npm 完全不同。npm 默认把每个依赖复制到各自的 node_modules 目录同一个版本的 lodash 在十个项目里就存十份。pnpm 则把依赖放进全局 store然后通过硬链接映射到项目里。查看当前 store 路径pnpm store path继续安装其他依赖后可以检查 store 的目录大小。你会发现 node_modules 里的文件大小看起来和真实文件一样但磁盘逻辑块却共享同一份物理数据。这就是为什么 pnpm 多个项目共用依赖时磁盘占用更小。5.4 运行脚本并构建pnpm run用法和 npm 基本一致pnpm run dev pnpm run buildpnpm 对依赖脚本的执行顺序和 npm 略有不同最大的区别是不会自动把 node_modules/.bin 里所有命令都暴露给任意脚本因此需要显式在 package.json 的 scripts 里声明。这个设计更安全但迁移老项目时偶尔会遇到“命令找不到”的情况解决办法是在 script 里显式写完整命令比如build: vite build而不是直接依赖全局命令。6. 功能测试与效果验证6.1 冷安装耗时对比要验证 Rust 重写带来的收益最实用的办法是做一个对照实验。步骤如下第一步准备一个依赖规模中等的真实项目确保有 package.json 和有效 lockfile。第二步分别记录 npm、旧版 pnpm、新版 pnpm 的安装耗时。为了减少误差可以先删除 node_modules 和缓存再进行冷安装。第三步通过命令计时。Linux 和 macOS 可以使用/usr/bin/time -v npm install /usr/bin/time -v pnpm installWindows 上可以用 PowerShell 的 Measure-CommandMeasure-Command { npm install } Measure-Command { pnpm install }更专业一点的工具是 hyperfine它可以自动执行多次并给出统计结果hyperfine --warmup 1 npm install pnpm install需要提醒的是对比时 Node 版本要一致registry 要一致缓存状态要一致。否则测出来的差异并不能完全归因于包管理器本身。6.2 热缓存耗时对比第二次安装时pnpm 的 store 已经存在对应文件新安装只需要创建硬链接。热缓存安装的耗时往往远低于冷安装。这也是很多人感觉 pnpm “第二次安装飞快”的原因。验证热缓存时不需要删除 node_modules直接重复执行安装命令pnpm install观察第二次执行时长再对比 npm 的第二次执行时长。正常情况下 pnpm 在热缓存场景下的优势会很明显因为 npm 仍然要复制大量文件。6.3 磁盘占用对比对比磁盘占用可以使用dudu -sh node_modules du -sh $(pnpm store path)同样一个项目npm 的 node_modules 如果占用 500MBpnpm 的 node_modules 里可能只有一部分硬链接实际唯一的物理文件在全局 store 中。如果你有多个项目共用依赖整体磁盘收益会非常可观。6.4 判断升级是否成功的标准升级 pnpm 12 后建议按以下清单确认状态pnpm --version输出新版本号项目安装依赖时没有出现 Node.js 版本不满足提示pnpm-lock.yaml能正常被解析没有出现 lockfile 冲突构建脚本pnpm run build执行成功有 postinstall 脚本的依赖出现在pnpm ignored-builds列表时能通过pnpm approve-builds正确放行应用可以正常启动页面和接口不受影响。如果以上都能通过基本可以认定 pnpm 12 在当前项目里是可用的。7. CLI 接口与批量工程化调用7.1 常用 CLI 命令速查pnpm 没有独立的 HTTP API 服务但它提供了非常完整的 CLI 接口。工程化中常用命令如下命令作用pnpm install安装依赖pnpm add pkg添加依赖pnpm remove pkg移除依赖pnpm run script执行 scriptspnpm store path查看全局存储路径pnpm store prune清理未被引用的缓存文件pnpm approve-builds放行指定的依赖构建脚本pnpm --filter pkg cmd在 Monorepo 中指定子包执行命令pnpm -r run script递归执行所有子包的 script在写自动化脚本的时候--filter和-r非常有用。比如只构建某个子包及其依赖pnpm --filter your-scope/website run build7.2 批量处理多个仓库如果你手上管理多个前端仓库可以写一个简单的 shell 脚本批量执行安装和构建#!/usr/bin/env bash set -e for dir in ./repos/*/; do echo 开始处理 $dir cd $dir pnpm install --frozen-lockfile pnpm run build cd - doneWindows 环境下可以使用 PowerShell 版本Get-ChildItem -Path ./repos -Directory | ForEach-Object { Write-Host 开始处理 $($_.FullName) Set-Location $_.FullName pnpm install --frozen-lockfile pnpm run build Set-Location .. }建议在批量任务中加上日志和失败重试逻辑。比如安装失败时输出错误并记录目录名方便后续单独处理。7.3 CI 流水线中的 pnpm在 GitHub Actions、GitLab CI 或 Jenkins 中pnpm 12 的核心用法是锁定 lockfile 并启用缓存。以 GitHub Actions 为例安装 pnpm 可以使用官方 action或者直接使用 npm 全局安装- uses: actions/setup-nodev4 with: node-version: 22 - run: npm install -g pnpm - run: pnpm install --frozen-lockfile - run: pnpm run buildCI 环境建议开启缓存。缓存目录一般指向 pnpm store 路径。你可以先执行一次pnpm store path拿到实际路径然后在 CI 缓存配置中把该目录缓存起来。这样后续流水线可以直接命中 store大幅减少下载时间。8. 资源占用与性能观察8.1 CPU 与内存观察Rust 重写带来的一个直观变化是安装依赖时 CPU 占用相对稳定。JavaScript 实现在解析大量依赖时容易产生较高的 GC 压力Rust 实现则更偏向低开销的静态内存分配。观察时可以同时打开系统资源监视器分别执行冷安装对比峰值 CPU 和内存占用。在 Linux 环境下可以使用/usr/bin/time -v查看最大驻留内存/usr/bin/time -v pnpm install 21 | grep Maximum resident在 Windows 上可以使用任务管理器观察进程列。一般来说同一台机器上pnpm 的安装峰值内存不会高于 npm但具体数值取决于项目依赖树规模。8.2 store 与 node_modules 的磁盘占用Rust 重写主要影响计算性能磁盘占用更取决于 pnpm 的硬链接设计。项目里的 node_modules 看起来很大是正常的因为硬链接在文件系统层面表现为多个路径指向同一物理文件。用df查看整个磁盘的剩余空间更能反映真实占用。8.3 降低资源占用的参数对于超大依赖树可以通过参数控制并发和重试策略pnpm install --network-concurrency 4--network-concurrency可以限制同时发起的网络请求数量降低网络抖动和大规模下载时的内存压力。如果某个依赖反复下载失败可以检查镜像源连通性而不是一味调大并发数。清理不再使用的 store 文件pnpm store prune这个命令会删除 store 中没有被任何项目引用的未使用包。注意执行前确认当前项目依赖已经安装完整否则清理后需要重新下载。9. 常见问题与排查方法9.1 pnpm 不是内部或外部命令这个问题在 Windows 上最常见现象是在 PowerShell 或 cmd 里执行pnpm -v提示命令不存在。可能原因是 npm 全局安装目录没有加入 PATH。先执行npm prefix -g拿到全局目录后把该目录的路径追加到系统 PATH。如果是通过 corepack 安装的还需要检查 corepack 是否执行了 enable或者是否使用corepack prepare激活了 pnpm。9.2 pnpm 下载失败或安装慢安装依赖时卡住多数是网络问题。第一步检查当前 registrypnpm config get registry如果指向默认源且下载很慢切换镜像源pnpm config set registry https://registry.npmmirror.com第二步清理旧缓存并重试pnpm store prune pnpm install第三步如果仍然失败查看具体报错是超时还是 TLS 证书问题。超时可以临时降低并发TLS 证书问题需要检查代理或内网镜像的证书配置。9.3 Node.js 版本不满足报错形如this version of pnpm requires at least node.js v22.13说明当前 Node.js 版本低于当前 pnpm 版本的要求。建议用 nvm、mise 或 fnm 安装更高版本的 Node.js然后重新执行 pnpm。不要尝试修改 pnpm 的 engines 校验文件绕过检查这样会造成不可预期的运行时问题。9.4 安装完成后提示 approve-builds如果你在 pnpm install 输出里看到类似Run pnpm approve-builds to pick which dependencies should be allowed to run这是 pnpm 10 以上版本的安全机制。默认情况下被标记的依赖不能自动执行 postinstall 脚本必须由你显式确认。处理方式pnpm approve-builds按提示选择允许的依赖即可。需要说明的是如果某些依赖依赖 postinstall 来编译原生模块不执行它可能导致安装不完整。但在批准之前先确认这些包来源可靠不要盲目放行所有脚本。9.5 pnpm run build 之后的产物如何部署到 Nginx这是前端工程里很常见的问题。pnpm 只管依赖安装和脚本执行构建完成后输出目录通常是 dist 或 build这取决于项目用的框架。假设你的 Vue/React 项目构建输出目录是 distNginx 配置可以参考server { listen 80; server_name example.com; root /var/www/my-app/dist; index index.html; location / { try_files $uri $uri/ /index.html; } }部署时建议先本地构建再把 dist 目录上传到服务器或者让 CI 流水线在服务器上直接执行pnpm install --frozen-lockfile pnpm run build随后把产物目录软链到 Nginx 的 root 指向的位置即可。9.6 如何删除或卸载 pnpm如果你确认不再使用 pnpm可以卸载全局安装。npm 全局安装的npm uninstall -g pnpm通过 corepack 安装的corepack uninstall pnpm另外如果项目里已经有 pnpm-lock.yaml但不希望继续使用 pnpm删除 lockfile 和 node_modules 后用 npm install 重新生成即可。注意不要直接手动删除整个 store推荐使用pnpm store prune清理。10. 最佳实践与使用建议第一保留一套最小可运行配置。把 Node.js 版本、pnpm 版本、registry 和 CI 缓存目录写进仓库文档团队内保持一致避免“我本机能跑CI 报错”的情况。第二lockfile 必须提交。pnpm-lock.yaml 是依赖复现的基准任何升级依赖的动作都应该通过pnpm add或pnpm update完成不要手动修改 lockfile。第三CI 环境固定版本。建议使用packageManager: pnpm12.x.x字段声明项目需要的 pnpm 版本再配合 corepack 自动切换防止开发机和 CI 使用不同版导致行为不一致。第四批量任务要加日志。批量处理多个子包或仓库时每执行完一个模块就输出摘要记录耗时、成功或失败。失败时保存日志文件便于定位。不要怕脚本啰嗦工程场景最怕的是失败后没有任何痕迹。第五store 缓存要合理利用。开发机可以共享全局 storeCI 环境要把 store 目录加入缓存但要注意缓存目录可能非常大定期执行pnpm store prune防止缓存膨胀。第六涉及原生依赖时要留意 approve-builds。像 sharp、bcrypt、node-sass 这类依赖通常有安装脚本或者需要下载二进制文件。第一次安装时如果被 pnpm 拦截不要慌先看包来源再用pnpm approve-builds选择放行。第七安全红线不能踩。不要盲目允许所有 postinstall 脚本不要在无法确认来源的情况下从非官方源安装包发布项目和部署前检查依赖许可证。包管理器的目标是高效管理依赖不是替你过滤恶意代码。11. 总结pnpm 12 值得升级吗如果当前项目还在用 npm升级到 pnpm 12 的主要收益是更快的安装速度和更小的磁盘占用。如果已经使用了 pnpm 10 或 11升级到 pnpm 12 的成本很低重点确认 Node.js 版本和 approve-builds 策略即可。Rust 重写带来的性能提升不需要在理论上争论太多拿一个真实项目跑一次冷安装、一次热安装、一次磁盘占用对比结果会非常直观。最容易踩的坑有三个第一Node.js 版本太旧导致 pnpm 无法启动第二postinstall 脚本被安全机制拦截后没有及时 approve导致原生依赖安装不完整第三镜像配置没有继承从 npm 迁移过来后发现下载依然很慢。这三个问题对应到本文第 4 章和第 9 章的内容基本都能解决。下一步建议做的事情也很明确先把 Node.js 版本和 pnpm 版本统一再给项目开一个分支做迁移测试用第 6 章的对比方法记录前后数据最后把 CI 流水线里的安装命令替换为pnpm install --frozen-lockfile。跑完一轮之后pnpm 12 在你的项目里到底快多少就不再是一个需要看别人跑分的问题了。