pnpm 和 Turborepo 的区别与选型
写作意图:把 pnpm 与 Turborepo 的职责边界讲清楚,避免在前端工程选型时把“包管理器”和“构建系统”混为一谈。
背景
在前端工程里,pnpm 和 Turborepo 经常同时出现在 monorepo 方案中。很多人在选型时会把它们放在一起比较,甚至会问:“我应该用 pnpm 还是用 Turborepo?”
这个问题的关键在于:它们并不是同一类工具。
更准确的划分是:
pnpm = 包管理器
Turborepo = 构建系统 / 任务编排器也就是说,pnpm 主要解决依赖安装和 workspace 管理问题;Turborepo 主要解决多包仓库中的任务执行、依赖顺序、并行调度和缓存问题。
问题
选型时真正需要回答的不是“二选一”,而是下面几个问题:
- 当前项目是单应用,还是 monorepo?
- 是否有多个
apps/和packages/? - 是否存在跨 package 的构建顺序?
- CI 中的
build、test、lint是否已经成为瓶颈? - 是否需要本地缓存或远程缓存来减少重复构建?
如果这些问题没有区分清楚,就容易出现两种偏差:
- 单项目过早引入 Turborepo,增加了配置复杂度;
- monorepo 变复杂后仍只用包管理器脚本硬撑,导致构建和 CI 越来越慢。
过程
pnpm 解决什么问题
pnpm 是 Node.js 生态中的包管理器,和 npm、Yarn 属于同一层级。
它主要负责:
安装依赖
生成 pnpm-lock.yaml
管理 node_modules
管理 workspace
链接本地 packagepnpm 的一个重要特征是依赖会存储在 content-addressable store 中。多个项目使用同一依赖时,不需要在每个项目中重复复制完整依赖文件。官方文档也把节省磁盘空间和 workspace 支持作为 pnpm 的核心能力之一。
在 monorepo 中,pnpm 通常通过 pnpm-workspace.yaml 定义工作区:
packages:
- "apps/*"
- "packages/*"这个配置的作用是告诉 pnpm:哪些目录下的项目属于同一个 workspace。
典型结构如下:
repo/
package.json
pnpm-lock.yaml
pnpm-workspace.yaml
apps/
web/
admin/
packages/
ui/
utils/在这个结构里,pnpm 负责把 apps/web、apps/admin、packages/ui、packages/utils 这些 package 管理起来。
Turborepo 解决什么问题
Turborepo 是面向 JavaScript / TypeScript 代码库的构建系统。它的核心能力不在于安装依赖,而在于执行任务。
它主要负责:
执行 build/test/lint/dev 等任务
根据 package 依赖关系安排执行顺序
并行运行可并行的任务
缓存任务输出
在 CI 或团队环境中复用构建结果例如一个仓库中存在这样的依赖关系:
apps/web 依赖 packages/ui执行构建时,合理顺序应该是:
先构建 packages/ui
再构建 apps/web如果 packages/ui 的输入没有变化,Turborepo 可以利用缓存跳过重复构建。这是它相对普通脚本编排的主要价值。
典型 turbo.json 配置如下:
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**"]
},
"test": {
"dependsOn": ["build"]
},
"lint": {}
}
}其中:
build.dependsOn: ["^build"]表示先构建依赖包;outputs告诉 Turborepo 哪些文件属于任务产物,可用于缓存判断;test.dependsOn: ["build"]表示测试前需要先完成当前 package 的构建。
这段配置展示的是核心思路,实际接入前还需要根据项目产物目录、框架类型和 CI 环境调整。
核心观点
1. pnpm 和 Turborepo 不是竞争关系
更准确的关系是:
pnpm 管依赖
Turborepo 管任务所以它们通常不是“用 pnpm 还是用 Turborepo”,而是:
是否只需要 pnpm
还是需要 pnpm + Turborepo2. pnpm 是基础设施
只要项目使用 Node.js 生态,pnpm 就可以作为 npm/yarn 的替代方案。
它适合:
- 单项目应用;
- 多 package workspace;
- 需要更严格依赖隔离的项目;
- 希望减少磁盘占用和安装时间的项目。
即使不用 Turborepo,也可以使用 pnpm workspace 管理 monorepo。
例如:
pnpm install
pnpm -r build
pnpm -r test这里的 pnpm -r 可以递归执行 workspace 中各 package 的脚本,适合复杂度不高的 monorepo。
3. Turborepo 是任务加速器和编排器
当仓库逐渐变大后,单纯递归执行脚本会暴露问题:
- 不该重跑的任务被重复执行;
- package 之间的构建顺序需要手动维护;
- CI 每次都完整跑一遍,耗时越来越长;
- 多个应用和共享包之间缺少统一任务图。
这时 Turborepo 的价值会更明显。
它适合解决:
哪些任务需要先跑
哪些任务可以并行
哪些任务可以复用缓存
哪些 package 受本次修改影响4. 不要过早引入 Turborepo
如果项目只有一个应用:
my-app/
package.json
src/通常只需要:
pnpm install
pnpm dev
pnpm build这类场景中,Turborepo 能带来的收益有限,反而会增加 turbo.json、任务产物声明、缓存边界等额外心智负担。
实践记录
场景一:单项目应用
推荐方案:
pnpm适用条件:
- 只有一个 Web 应用或 Node.js 服务;
- 没有共享 package;
- 构建、测试、lint 时间可接受;
- CI 没有明显性能瓶颈。
示例命令:
pnpm install
pnpm dev
pnpm build
pnpm test这时的重点是把包管理、锁文件和脚本规范做好,不需要为了“工程化完整”而额外引入 Turborepo。
场景二:小型 monorepo
推荐方案:
pnpm workspace 起步
Turborepo 可选典型结构:
repo/
apps/
web/
packages/
ui/
utils/先用 pnpm-workspace.yaml 管理 workspace:
packages:
- "apps/*"
- "packages/*"如果任务规模还小,可以先使用:
pnpm -r build
pnpm -r lint
pnpm -r test当出现下面的问题时,再引入 Turborepo:
- 构建时间明显变长;
- 每次 CI 都重复构建未变化的 package;
- package 之间依赖顺序开始复杂;
- 多个 app 共享多个内部 package;
- 团队希望复用本地或远程缓存。
场景三:中大型 monorepo
推荐方案:
pnpm workspace + Turborepo典型结构:
repo/
apps/
web/
admin/
docs/
packages/
ui/
auth/
api-client/
eslint-config/
tsconfig/这类项目中,pnpm 和 Turborepo 的职责可以这样划分:
| 职责 | 工具 |
|---|---|
| 安装依赖 | pnpm |
| 管理 workspace | pnpm |
| 链接内部 package | pnpm |
| 执行 build/test/lint | Turborepo |
| 处理任务依赖顺序 | Turborepo |
| 本地缓存和远程缓存 | Turborepo |
| CI 增量构建 | Turborepo |
根目录 package.json 可以这样组织:
{
"scripts": {
"build": "turbo build",
"test": "turbo test",
"lint": "turbo lint"
},
"devDependencies": {
"turbo": "latest"
}
}这段配置只是示例,实际项目不建议直接长期使用 latest,应根据团队版本管理策略锁定版本范围。
选型表
| 项目状态 | 推荐选择 |
|---|---|
| 单个前端项目 | pnpm |
| 单个 Node.js 服务 | pnpm |
| 只有少量共享包 | pnpm workspace |
| 小型 monorepo,CI 不慢 | pnpm workspace,暂不引入 Turborepo |
| 多 app + 多 packages | pnpm workspace + Turborepo |
| CI 构建时间明显变长 | 引入 Turborepo |
| 需要任务缓存和远程缓存 | 引入 Turborepo |
| 需要更强项目图、代码生成、插件体系 | 可进一步评估 Nx |
结论
pnpm 和 Turborepo 的区别可以压缩成一句话:
pnpm 是包管理器,Turborepo 是任务编排和缓存工具。实际选型建议是:
单项目:pnpm
小型 monorepo:pnpm workspace
中大型 monorepo:pnpm workspace + Turborepo不要把 Turborepo 当成 pnpm 的替代品,也不要把 pnpm workspace 当成完整的构建系统。前者负责依赖和 workspace,后者负责任务图、缓存和执行效率。
当前文章基于本次讨论和官方文档整理,未对具体项目进行实际构建验证。落地到真实仓库前,还需要根据项目的 package 数量、构建耗时、CI 流程、缓存产物和团队协作方式做进一步验证。
参考资料
- pnpm 官方文档:pnpm 说明其依赖会存储在 content-addressable store 中,并强调安装速度、节省磁盘和 workspace 支持。
- Turborepo 官方文档:Turborepo 定位为 JavaScript / TypeScript 代码库的高性能构建系统,并提供任务并行、缓存和 monorepo 工作流能力。