12k
All articles

你应该从 npm 切换到 pnpm 吗?

要从 npm 切换到 pnpm 吗?比较依赖隔离、磁盘节省、工作区协作和 pnpm 11 的构建脚本审批。

OpenReplay Team
OpenReplay Team
你应该从 npm 切换到 pnpm 吗?

你可能在 monorepo 配置、CI 配置以及框架文档中看到过 pnpm 的身影。也许有同事对它推崇备至。但对于你的日常前端工作来说,切换是否真的值得这份折腾,还是说它解决的只是你尚未遇到的问题?

以下是你真正需要了解的内容。

核心要点

  • pnpm 使用基于内容寻址的存储,结合硬链接和符号链接,消除了幻影依赖问题,并减少了多项目下的磁盘占用。
  • pnpm 11 延续了 pnpm 10 中引入的生命周期脚本限制,需要通过 pnpm approve-builds 显式授权,以降低供应链风险。
  • 工作区过滤功能以及 workspace:* 协议使 pnpm 非常适合使用 Turborepo 或 Nx 的 monorepo。
  • 通过 package.json 中的 packageManager 字段锁定包管理器,并在 CI 中使用 --frozen-lockfile 实现确定性安装。
  • pnpm 在 monorepo 和多项目场景中表现尤为出色;对于单包应用,继续使用 npm 通常也无妨。

pnpm 与 npm 的本质区别

pnpm 不仅仅是更快的 npm,真正的差异在于结构层面。

npm 创建的是扁平化的 node_modules 目录,这意味着你的代码可能会意外引入你从未声明为依赖的包——这就是所谓的幻影依赖问题。pnpm 则采用基于内容寻址的存储,结合硬链接和符号链接,因此只有你显式声明的依赖才能在根级别访问。这使得依赖解析更加严格,也更具可复现性。

另一个主要差异是磁盘占用。pnpm 全局只存储每个包版本一次,并通过硬链接将其链入你的项目。如果你在本地维护多个 Next.jsVite 项目,就不会在每个项目中重复占用数百兆的空间。

如需深入了解其存储模型和依赖隔离机制,值得浏览一下官方的 pnpm 与 npm 对比文档

pnpm 11 与安全优先的转向

pnpm 11 于 2026 年 4 月发布,延续了近几个主要版本中逐渐形成的趋势:将安全性和确定性置于纯粹速度之上。

在切换之前最需要了解的关键行为是:pnpm 现在默认会阻止依赖项的生命周期脚本执行,除非显式授权。 这就是 pnpm 10 中引入的 pnpm approve-builds 工作流。如果你安装了一个带 postinstall 脚本的包——这在需要编译原生二进制的包(如 sharpesbuildcanvas)中很常见——安装会成功完成,但该脚本要等到你授权后才会运行。

这会让那些期望一切开箱即用的开发者感到意外。你可以交互式地运行 pnpm approve-builds,或在 pnpm-workspace.yaml 中配置允许构建的依赖项:

onlyBuiltDependencies:
  - sharp
  - esbuild

这是一种有意的权衡:前期多些摩擦,后期少些供应链意外。

工作区与 Monorepo 工具链

这正是 pnpm 明显领先的地方。npm 工作区也能用,但在更大规模的项目中,pnpm 的工作区实现更加符合工程实践。

你在 pnpm-workspace.yaml 文件中定义包:

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

然后即可开箱即用地享受强大的过滤功能:

pnpm --filter @myapp/ui build
pnpm --filter "...^@myapp/ui" test  # 在依赖 ui 的包中运行测试

对于使用 TurborepoNx 的团队,pnpm 的工作区协议(workspace:*)集成顺畅,能让内部依赖关系保持显式清晰。

使用 packageManager 字段锁定版本

无论你是否使用 Corepack,有一个实用的做法是:在 package.json 中添加 packageManager 字段,标明项目所期望的包管理器及其版本。

{
  "packageManager": "pnpm@11.0.0"
}

在启用了 Corepack 的环境中可以强制执行该约定,即使未启用 Corepack,它也能向团队和 CI 系统清晰地传达意图。

与 GitHub Actions 集成的 CI 配置

- uses: pnpm/action-setup@v6
  with:
    version: 11
- uses: actions/setup-node@v4
  with:
    node-version: 22
    cache: 'pnpm'
- run: pnpm install --frozen-lockfile

在 CI 中始终使用 --frozen-lockfile。它能防止 lockfile 被静默修改,并确保安装的确定性。

官方的 pnpm CI 文档 还提供了适用于 GitHub Actions、GitLab CI、CircleCI、Azure Pipelines 和 Bitbucket Pipelines 的示例。

何时值得切换

如果你在 monorepo 中工作、管理多个本地项目,或希望默认获得更严格的依赖隔离,那么 pnpm 是最合适的选择。pnpm approve-builds 的安全模型对于注重供应链卫生的团队来说确实非常有用。

如果你只是维护一个单包应用,而 npm 用得也挺顺手,那么迁移的摩擦可能并不值得。npm 工作区对于直观的项目结构已经足够成熟,作为生态系统默认值仍然具有实实在在的价值。

结语

坦率地说:先在新项目中尝试 pnpm。其命令几乎完全一致,lockfile 可读性强,而且大多数前端框架——包括 Next.js、Vite 和 Astro——都无需额外配置即可支持。如果它的严格性和磁盘节省契合你的工作流,再扩展到现有项目就是一个轻松得多的决定。

常见问题

我可以把现有的 npm 项目切换到 pnpm 而不出问题吗?

通常可以。删除 node_modules 和 package-lock.json,然后运行 pnpm import 转换你的 lockfile,接着执行 pnpm install。要留意你的代码可能在 npm 扁平结构下依赖的幻影依赖——pnpm 会以缺失导入的形式暴露出来,你只需将它们显式添加到 package.json 即可修复。

pnpm approve-builds 与直接允许所有脚本有什么区别?

approve-builds 是一个允许运行生命周期脚本的包白名单。你可以完全关闭这道关卡,但这样做会重新引入 pnpm 11 旨在缓解的供应链风险。推荐的做法是仅授权那些你信任且确实需要 postinstall 步骤的包,例如原生二进制编译器。

pnpm 能兼容所有 npm 包吗?

绝大多数包无需改动即可使用。问题主要出现在那些假定扁平 node_modules 结构或依赖幻影依赖的包上。大多数流行库早已修复了这些问题。如果有兼容性问题,可在 .npmrc 中通过 public-hoist-pattern 设置为特定包模拟 npm 风格的提升,作为应急手段。

实际使用中 pnpm 比 npm 更快吗?

对于冷安装,得益于并行解析和内容寻址存储,pnpm 通常更快。对于多个共享依赖的项目的热安装,差距会非常明显,因为 pnpm 通过硬链接复用已下载的包。在带有完整缓存的 CI 环境中差距会缩小,但 pnpm 通常仍保持一定优势。

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — self-hosted, with full data ownership.

Star on GitHub

We use cookies to improve your experience. By using our site, you accept cookies.