这个月 pnpm 发了 v11。本来想等 v12 的 Rust 引擎一起写,但试了一圈下来有几个改动确实值得单独说说。如果你还在犹豫要不要从 pnpm 10 升上来,这篇应该能帮你省点时间。
先说我碰到的第一个坑
升级完第一件事——Node.js 必须 >= 22。
倒也不算过分的要求,Node 22 去年就 LTS 了。问题是很多项目到现在还在用 20,特别是一些历史遗留项目或者云函数那边有运行时限制。我自己有个项目就是 node 20,跑 pnpm install 直接报错。
所以如果你是那种 CI 镜像比较久没动的,或者项目 Dockerfile 还挂在 centos 上(pnpm 11 要求 glibc 2.27+),升级前多留个心眼。
另外 pnpm 11 自己变成纯 ESM 了,CommonJS 的分发包直接砍掉。对日常使用来说没什么感觉,你正常 pnpm install 就完事。但如果你写的脚本有依赖 pnpm 内部模块,那得注意一下。
供应链安全这块,总算不是摆设了
pnpm 从 v10 开始就有安全方向的动向了,但 v11 算是一次正经的落地。
新包有冷却期
之前 Shai-Hulud 那种供应链攻击大家应该还有印象——攻击者先发几个合法版本积累信誉,然后突然放一个带后门的版本,CI 自动拉取,直接中招。
pnpm 11 的做法简单粗暴:新包默认延迟 24 小时才能被解析安装。minimumReleaseAge 默认 1440 分钟,等于给攻击者的快攻踩了脚刹车。就算有人发了恶意版本,你有一整天等社区去发现它。
24 小时是不是太长?如果有紧急修复要靠刚发的新版本,可以用 pnpm audit --fix 加白名单绕过去。或者 pnpm-workspace.yaml 里写一行 minimumReleaseAge: 0 直接关掉。看你自己权衡。
非标准来源默认阻断
blockExoticSubdeps 现在默认是开的。exotic 指的是从 Git 仓库直接拉、或者 tarball URL 装的传递依赖。这些不走 npm 注册表,registry 管不到,攻击者最喜欢拿来偷偷塞东西。
说实话,多数项目不会大量用到这种 exotic 依赖,所以这个默认动作影响面不大。但确实堵了一个口子。
构建脚本统一管了
之前 pnpm 有五个散落的配置项控制构建脚本执行,v11 统一成了 allowBuilds 一个字段:
# pnpm-workspace.yaml
allowBuilds:
electron: true
core-js: false
esbuild: false
生命周期脚本攻击一直是被用得最多的路子之一。这个改动本身不复杂,但好处是看一眼配置文件就知道哪些包有执行权限,不用去翻 docs 找那几个参数在哪。
终于不用再调 npm publish 了
从 pnpm 出生那天起,pnpm publish 底层一直在调 npm publish。v11 终于把这个历史包袱甩掉了。publish、login、logout、view、deprecate、unpublish、dist-tag、version 这些命令全部原生实现,不再依赖 npm CLI。
实际好处是两个:
- 少一层依赖,少一个攻击面。以前 npm CLI 出 bug 可能波及 pnpm 用户,现在没这个问题了。
- OTP 环境变量从
NPM_CONFIG_OTP改成了PNPM_CONFIG_OTP,Web 认证支持扫码登录。
真用起来感觉清爽不少,不依赖 npm CLI 的发布流程干净很多。
SQLite 替掉了上百万个 JSON 文件
pnpm 的 Store 架构升到了 v11。原来 store 里每个包会对应一个 package.json 文件,项目多了数量级上去就是天文数字。现在全合并到单个 SQLite 数据库,manifest 信息直接存进去,安装时不用反复读文件。
还有几个值得说的小改进:
- HTTP 请求从 node-fetch 换成了 undici,顺手做了 Happy Eyeballs(双栈连接)
- 已知大小的 tarball 下载预分配内存,省去反复拷贝
- CAS 文件直接写到内容寻址路径,不经过临时文件再重命名的流程。官方说冷安装能少约三万次 rename 系统调用
- 全局虚拟存储开了以后,95% 的包在 Node.js 升级或者架构变更时不用重新导
实际体验:有缓存和 lockfile 的 warm install 跑到了 2.3 秒。比以前快了不少。
全局安装终于隔开了
pnpm add -g 现在行为变了。每个全局包有自己的目录、package.json、node_modules 和 lockfile,互相隔开。之前在全局装两个 peer dep 冲突的工具会打架的情况,应该会少很多。
二进制文件放在 PNPM_HOME/bin 子目录下,不再直接丢进 PNPM_HOME。shell 补全也干净了点。
SBOM 顺带提一下
pnpm 11 加了 pnpm sbom 命令,能生成 CycloneDX 1.7 或 SPDX 2.3 格式的软件物料清单。如果你在做合规审计或者客户指名要 SBOM,那用得上。不然大概率用不着。
审计的两个变化
pnpm audit 有两个地方变了:
--fix=update:直接在 lockfile 里更新修复版本,不再靠加 overrides 硬修。这个方法直觉多了。- 切到了 GHSA 标识。npm 注册表废弃了老的 CVE 审计端点,所以审计脚本里有 CVE 条目的,需要换成对应的 GHSA。
配置搬家
.npmrc 现在只管鉴权和注册表设置。pnpm 特有的配置全部搬到 pnpm-workspace.yaml 或新的全局 config.yaml。npm_config_* 环境变量也换成了 pnpm_config_*。
官方估计也知道这些变动烦人,准备了一个 pnpm-v10-to-v11 codemod,大部分配置能自动转换。
Rust 引擎快来了
pnpm 团队在搞下一代引擎 Pacquet,基于 Rust 重写,预计 v12 落地。先做 fetch 和 link,后面再覆盖依赖解析。
官方放了一组数据:lockfile-only 无缓存安装,Rust 引擎 3.1 秒,pnpm 11 是 4.7 秒。Warm install 更夸张——902 毫秒对 2.3 秒。虽然还没到生产就绪的程度,但这数据确实诱人。
package manager 这几年的竞争,从 npm 一家独大,到 Yarn 加速,到 pnpm 效能和安全一起抓,再到 Bun 杀进来——越来越有意思了。
总结:新项目直接上 pnpm 11;现有项目啃下 Breaking Changes 清单再规划。最需要留意的是 Node.js 版本和配置迁移,别的大头可以用 codemod 扫掉。
如果你觉得这篇有用,欢迎来我的博客 Aura Imagai 逛逛,主要写前端、AI 和工程化相关的东西。
参考来源: