Monorepo 需要什么(真实需求)
发布: (2026年2月25日 GMT+8 23:35)
4 分钟阅读
原文: Dev.to
Source: Dev.to
Monorepo 包管理器应该处理的内容
- 工作区(多个 package/app)
- 大型仓库的快速安装
- 确定性的 lockfile(在所有地方使用相同的依赖)
- 良好的 CI 缓存
- 最小化“依赖怪异”(幽灵依赖、提升意外)
- 流畅的开发者工作流(跨 package 运行脚本、过滤等)
pnpm 的优势
全局内容可寻址存储
- pnpm 在全局存储中为每个包的每个版本只保留一份副本。
- 每个项目通过硬链接指向
node_modules。 - 结果: 重复安装非常快,庞大的 monorepo 能受益良多。
更小的仓库体积
- 使用 npm 时,每个工作区可能在子项目之间重复相同的包。
- 使用 pnpm 时,包会从全局存储复用。
- 结果: 仓库体积更小,Docker/CI 层构建更快。
严格的依赖处理
- 包不能导入未在自身
package.json中声明的依赖。 - 这可以防止经典的 monorepo bug:“在我机器上能跑是因为被提升了…但 CI 后来就坏了。”
人性化的工作区命令
- 在所有 package 中运行脚本。
- 过滤后仅在受影响的 package 上运行。
- 递归的清理安装/构建。
高效的缓存
- 由于 pnpm 的存储可复用,缓存更简单、更有效。
- 在许多 CI 流水线中,pnpm 安装在第一次缓存后会始终保持更快。
npm 的考虑因素
- npm 默认随 Node 一起提供。
- 一些旧工具和脚本假设 npm 的提升行为,使得在某些企业环境中 npm 显得“乏味且安全”。
- 如果团队已经熟悉 npm,且仓库规模较小,npm 工作区可能已经“够用了”。
- npm 经常提升依赖,允许代码导入未声明的包,这会把缺失依赖的问题隐藏到后期才显现。
在 pnpm 与 npm 之间的选择
| 方面 | npm | pnpm |
|---|---|---|
| 依赖处理 | 提升的依赖即使未声明也可能可用 | 通过符号链接结构强制正确性;未声明的依赖会提前被捕获 |
| 仓库体积 | 由于重复的包而更大 | 由于共享存储而更小 |
| 安装速度 | 对大型仓库尤其是重复安装时较慢 | 更快,特别是在第一次缓存之后 |
| 适用场景 | 小型仓库、依赖提升的环境 | 大型仓库、重复安装、重视严格性的团队 |
如果你想要:
- 大型 monorepo 的更快、确定性的安装
- 严格的依赖强制
- 更好的 CI 缓存
请选择 pnpm。
如果你想要:
- 与期望提升行为的旧工具兼容
- 小型仓库的简易性
请选择 npm。
推荐的 pnpm 设置
- 在仓库根目录保留单一的
pnpm-lock.yaml。 - 使用 pnpm 工作区。
- 为了团队一致性,固定 pnpm 版本,例如:
{
"packageManager": "pnpm@9.1.2"
}
- 在
pnpm-workspace.yaml中定义工作区布局:
packages:
- "apps/*"
- "packages/*"
我在 2026 年的默认选择: 几乎所有新 monorepo 都使用 pnpm。