还在用npm?该换pnpm了
如果你到 2026 年还在把 npm 当默认包管理器用,我完全理解。npm 能用,它是默认选项,也是几乎所有教程都会教的工具。它跟着 Node.js 一起安装,你只要会npm install、npm run、npm publish,大多数时候就够了。切换包管理器听起来也不像什么高优先级任务,通常很容易被一直往后拖。
但 pnpm 11 在 2026 年 4 月底发布后,npm 和 pnpm 之间的差距,已经从“值得考虑”变成了“很难忽视”。
而且,这个差距现在不只是速度问题。
更关键的是安全。
为什么大多数开发者从不质疑 npm?
最真实的答案是:惯性。
npm 跟 Node.js 一起自带,所以它往往是你接触到的第一个包管理器。你学会了npm install,学会了npm run,学会了npm publish,它也确实能完成工作。
出问题时,Stack Overflow 上到处都是答案。新同事加入项目时,他们也基本都懂 npm。
熟悉,确实很省心。
但这种熟悉感背后,也有成本。只是这个成本平时不明显,只有真正出事时,你才会意识到它有多贵。
尤其是在 2026 年,供应链攻击越来越复杂,出事的概率也越来越高。
分享一个正版GPT5.5 目前 0.2 倍率, 关注公众号后,在后台回复:airealy 即可自动获取兑换码及使用方式。
npm 最大的问题,很多人没认真看过
一个典型的 npm 供应链攻击,通常是这样发生的。
某个维护者的 token 被盗。攻击者发布一个恶意 patch,比如某个热门工具包的v1.2.1。你的 CI/CD pipeline 定时运行,通过^1.2.0这样的 semver range 自动拉取最新版本,然后执行其中的postinstall脚本。
这个脚本会在你毫无感知的情况下,从环境变量里偷走 secrets。
等 npm registry 发现异常,并把恶意版本下架时,你的 pipeline 可能早就已经中招了。
这种模式并不是理论攻击。
最近类似 Mini Shai-Hulud 这样的攻击活动,就同时影响了 npm、PyPI 和 Packagist 上的包。攻击者利用安装阶段的 hooks 下载 Bun runtime,并运行混淆后的凭据窃取程序,目标就是 CI/CD secrets。
也就是说,真正危险的不是你手动安装了什么奇怪包。
而是自动化流水线太快了。
快到恶意版本还没被社区发现,你的系统就已经把它装进来了。
pnpm 到底哪里不一样?
pnpm 这些年一直比 npm 更快,也更省磁盘。
如果你没用过,可以简单理解为:pnpm 使用 content-addressable store 和 hard links,在多个项目之间共享同一个包版本。
所以,你不会在 5 个仓库里重复下载 5 份相同版本的 React。
仅这一点,就能节省不少磁盘空间和安装时间。
但到了今天,速度和效率已经只是基础能力。
pnpm 11 真正有意思的地方,是它把安全能力变成了默认行为。
pnpm 11:安全不是配置项,而是默认值
24 小时规则:minimumReleaseAge
这是 pnpm 11 里最有影响力的变化,而且设计得非常简单。
当你运行pnpm install时,任何发布不到 24 小时的新版本,默认都不会被解析安装。它会先进入一个冷却期。
# pnpm-workspace.yaml pnpm: minimumReleaseAge: 1440 # default in pnpm 11 (minutes = 1 day)为什么这很重要?
因为大多数供应链攻击靠的就是速度。
攻击者的窗口很窄:发布恶意版本,等待自动化 pipeline 在 registry 发现并移除之前把它拉下来。这个窗口通常只有几个小时。
24 小时冷却期意味着什么?
意味着当 pnpm 开始考虑安装某个新发布版本时,安全社区已经有时间发现、标记并处理它。
像 Socket.dev、Snyk 这类工具都会持续监控 npm registry。很多恶意版本,往往会在几个小时内被发现并下架。
当然,这不代表你绝对免疫。
但它能显著缩小爆炸半径。
如果你确实需要立刻拉取某个合法的紧急版本,也可以为特定包设置排除:
pnpm: minimumReleaseAge: 1440 minimumReleaseAgeExclude: - "@types/*" - "my-trusted-internal-package"npm 没有等价能力。
它的逻辑更直接:现在有什么,就装什么。
堵住隐藏攻击路径:blockExoticSubdeps
还有一个攻击面,很多开发者平时根本不会注意:exotic subdependencies。
你从 npm registry 安装的直接依赖,它自己可能会拉取来自 GitHub 仓库、tarball URL,甚至私有 SSH 链接的传递依赖。
这些“exotic”来源会绕过标准 registry 审计链路。
它们更难扫描,更难验证,也更容易被用来把恶意代码藏进依赖树深处。
pnpm 11 默认设置了:
pnpm: blockExoticSubdeps: true # now the default也就是说,传递依赖只能从你配置的 registry 解析。如果某个 subdependency 试图从 GitHub repo 或原始 URL 拉取,pnpm 会直接停止安装。
npm 并不会强制这个边界。
你依赖树里的任何包,都可能从各种地方拉它自己的子依赖。
控制什么可以执行:allowBuilds
生命周期脚本,比如preinstall、postinstall、prepare,正是大多数恶意软件在安装阶段执行代码的入口。
攻击者发布一个包,加上一个postinstall脚本。你运行安装命令时,这个脚本就会立刻以当前用户权限执行。
pnpm 10 已经在这里做过一个很强的选择:默认禁用依赖中的自动postinstall执行。
pnpm 11 又进一步,把之前分散的旧配置,比如onlyBuiltDependencies、neverBuiltDependencies、ignoredBuiltDependencies,统一成一个更清晰的allowBuildsmap:
pnpm: allowBuilds: "electron": true "esbuild": true "core-js": false你可以明确指定哪些依赖允许运行 build scripts。
其他全部默认阻止。
这会强制团队建立一种明确的信任模型:你不再只是希望某个包没有恶意postinstall,而是主动决定哪些包有资格在你的机器上执行代码。
npm 的做法则宽松得多。
生命周期脚本默认全部执行,除非你传--ignore-scripts或手动配置例外。
但这个 flag 会破坏不少合法工具,所以大多数团队根本不会长期启用它。
信任策略:trustPolicy
pnpm 11 还引入了trustPolicy设置。
当它设置为no-downgrade时,如果某个包的信任等级相比历史版本下降,pnpm 会拒绝安装。
比如,之前版本由 verified publisher 发布,但最新版本缺少 provenance 或签名,这就会被视为异常。
pnpm: trustPolicy: no-downgrade这听起来好像很小众。
但它其实对应一个非常真实的攻击面。
攻击者如果攻破维护者账号,经常会发布一个新版本,而这个版本未必具备之前同样的签名流程。
信任策略检查,就能自动抓住这种异常。
pnpm 11 还改了什么?
供应链安全当然是最大亮点,但 pnpm 11 还有几个值得注意的改进。
原生发布和 registry 命令
以前,pnpm publish以及相关命令底层还会委托 npm CLI。
pnpm 11 开始原生处理这些能力,包括 login、logout、view、deprecate、unpublish、dist-tag 和 version。
也就是说,不再依赖 npm。
内置 SBOM 生成
pnpm sbom可以生成 Software Bill of Materials,支持 CycloneDX 1.7 或 SPDX 2.3 JSON 格式。
对受监管行业,或者需要做供应商安全审查的团队来说,这个能力非常实用。
SQLite-backed store,也就是 Store v11
之前的 store 会用每个包一个 JSON 文件来跟踪索引。
Store v11 改成了单个 SQLite 数据库。
结果是 syscall 更少,安装速度也更快。
在 warm install,也就是缓存和 lockfile 都存在的情况下,pnpm 11 大约 2.3 秒就能完成安装。
隔离的全局安装
现在运行pnpm add -g,每个全局包都会被放进自己的隔离目录里,并拥有独立的node_modules和 lockfile。
这可以减少全局安装工具之间奇怪的版本冲突。
要求 Node.js 22
pnpm 11 不再支持 Node.js 18、19、20 和 21。
它现在也是 pure ESM。
如果你的 CI 还停留在旧 Node 版本,这一点在迁移前需要提前规划。
pnpm 12 接下来会带来什么?
这个也值得提一下,因为方向很重要。
pnpm v12 计划引入 Pacquet,一个基于 Rust 的安装引擎,用来处理 fetching 和 linking。
早期 benchmark 显示,warm install 可能会从 pnpm 11 的 2.3 秒降到 1 秒以内。
cold install,也就是没有缓存、没有node_modules的情况,则可能从 4.7 秒降到大约 3.1 秒。
Pacquet 现在还没准备好用于生产环境。
但方向已经很明显:pnpm 正在以 npm 没有展现出的力度,持续投入性能优化。
怎么从 npm 迁移到 pnpm?
如果你已经被说服了,迁移其实很简单。
先安装 pnpm:
npm install -g pnpm或者使用 standalone installer:
curl -fsSL https://get.pnpm.io/install.sh | sh -然后导入现有 npm 项目:
pnpm import这会把package-lock.json转换成pnpm-lock.yaml。
你的package.json不会变化。
接着安装依赖:
pnpm install然后更新 CI/CD 脚本。
把npm install替换成pnpm install。
把npm run替换成pnpm run,或者直接用pnpm。
大多数 npm scripts 都可以直接兼容。
最后,在pnpm-workspace.yaml里配置你的安全默认值:
pnpm: minimumReleaseAge: 1440 blockExoticSubdeps: true trustPolicy: no-downgrade allowBuilds: "electron": true "esbuild": true # add other packages that legitimately need build scripts真正需要注意的是:pnpm 默认使用更严格的模块解析。
如果你的代码访问了某个包,但它没有声明在package.json里,也就是所谓 phantom dependencies,那迁移后可能会报错。
不过,这其实是好事。
它会帮你暴露隐藏依赖,只是你可能需要在代码库里做一点小修补。
npm vs pnpm:快速对比
能力 | npm | pnpm |
|---|---|---|
磁盘使用 | 多项目重复安装包 | 共享 store + hard links |
安装速度 | 基准水平 | 更快,v11 使用 SQLite store |
minimumReleaseAge | 不支持 | v11 默认 24 小时 |
blockExoticSubdeps | 不支持 | v11 默认开启 |
build script 控制 | 默认全部执行 | 通过 allowBuilds 显式允许 |
trust policy | 不支持 | 支持 no-downgrade |
SBOM 生成 | 不支持 | v11 内置 |
原生 publish | 支持 | v11 支持,不再依赖 npm |
你到底该不该换?
如果你只是做个人项目,或者一个没有 CI/CD pipeline 的小应用,紧迫性没那么高。
npm 仍然会继续工作。
但如果你在管理生产基础设施,运行会自动安装依赖的 CI/CD pipeline,或者和团队一起维护多个仓库,那么 pnpm 11 的安全默认值就不是“锦上添花”。
它们更像是一层本来早就该有的防护。
如果 npm 三年前就默认提供这些能力,很多高关注度的供应链事件,可能根本不会扩大到那个程度。
迁移成本很低。
后续维护差异也不大。
但安全姿态的提升,非常明显。
pnpm 其实已经当了很久更好的包管理器。
而到了 v11,它也成了更安全的那个。
最后:
精通 React 面试:从零到中高级(针对面试回答)
CSS终极指南
Vue 设计模式实战指南
20个前端开发者必备的响应式布局
深入React:从基础到最佳实践完整攻略
python 技巧精讲
React Hook 深入浅出
CSS技巧与案例详解
vue2与vue3技巧合集
