Skip to content

pnpm 和 Turborepo 的区别与选型

写作意图:把 pnpm 与 Turborepo 的职责边界讲清楚,避免在前端工程选型时把“包管理器”和“构建系统”混为一谈。

背景

在前端工程里,pnpmTurborepo 经常同时出现在 monorepo 方案中。很多人在选型时会把它们放在一起比较,甚至会问:“我应该用 pnpm 还是用 Turborepo?”

这个问题的关键在于:它们并不是同一类工具。

更准确的划分是:

txt
pnpm       = 包管理器
Turborepo  = 构建系统 / 任务编排器

也就是说,pnpm 主要解决依赖安装和 workspace 管理问题;Turborepo 主要解决多包仓库中的任务执行、依赖顺序、并行调度和缓存问题。

问题

选型时真正需要回答的不是“二选一”,而是下面几个问题:

  1. 当前项目是单应用,还是 monorepo?
  2. 是否有多个 apps/packages/
  3. 是否存在跨 package 的构建顺序?
  4. CI 中的 buildtestlint 是否已经成为瓶颈?
  5. 是否需要本地缓存或远程缓存来减少重复构建?

如果这些问题没有区分清楚,就容易出现两种偏差:

  • 单项目过早引入 Turborepo,增加了配置复杂度;
  • monorepo 变复杂后仍只用包管理器脚本硬撑,导致构建和 CI 越来越慢。

过程

pnpm 解决什么问题

pnpm 是 Node.js 生态中的包管理器,和 npmYarn 属于同一层级。

它主要负责:

txt
安装依赖
生成 pnpm-lock.yaml
管理 node_modules
管理 workspace
链接本地 package

pnpm 的一个重要特征是依赖会存储在 content-addressable store 中。多个项目使用同一依赖时,不需要在每个项目中重复复制完整依赖文件。官方文档也把节省磁盘空间和 workspace 支持作为 pnpm 的核心能力之一。

在 monorepo 中,pnpm 通常通过 pnpm-workspace.yaml 定义工作区:

yaml
packages:
  - "apps/*"
  - "packages/*"

这个配置的作用是告诉 pnpm:哪些目录下的项目属于同一个 workspace。

典型结构如下:

txt
repo/
  package.json
  pnpm-lock.yaml
  pnpm-workspace.yaml
  apps/
    web/
    admin/
  packages/
    ui/
    utils/

在这个结构里,pnpm 负责把 apps/webapps/adminpackages/uipackages/utils 这些 package 管理起来。

Turborepo 解决什么问题

Turborepo 是面向 JavaScript / TypeScript 代码库的构建系统。它的核心能力不在于安装依赖,而在于执行任务。

它主要负责:

txt
执行 build/test/lint/dev 等任务
根据 package 依赖关系安排执行顺序
并行运行可并行的任务
缓存任务输出
在 CI 或团队环境中复用构建结果

例如一个仓库中存在这样的依赖关系:

txt
apps/web 依赖 packages/ui

执行构建时,合理顺序应该是:

txt
先构建 packages/ui
再构建 apps/web

如果 packages/ui 的输入没有变化,Turborepo 可以利用缓存跳过重复构建。这是它相对普通脚本编排的主要价值。

典型 turbo.json 配置如下:

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 不是竞争关系

更准确的关系是:

txt
pnpm 管依赖
Turborepo 管任务

所以它们通常不是“用 pnpm 还是用 Turborepo”,而是:

txt
是否只需要 pnpm
还是需要 pnpm + Turborepo

2. pnpm 是基础设施

只要项目使用 Node.js 生态,pnpm 就可以作为 npm/yarn 的替代方案。

它适合:

  • 单项目应用;
  • 多 package workspace;
  • 需要更严格依赖隔离的项目;
  • 希望减少磁盘占用和安装时间的项目。

即使不用 Turborepo,也可以使用 pnpm workspace 管理 monorepo。

例如:

bash
pnpm install
pnpm -r build
pnpm -r test

这里的 pnpm -r 可以递归执行 workspace 中各 package 的脚本,适合复杂度不高的 monorepo。

3. Turborepo 是任务加速器和编排器

当仓库逐渐变大后,单纯递归执行脚本会暴露问题:

  • 不该重跑的任务被重复执行;
  • package 之间的构建顺序需要手动维护;
  • CI 每次都完整跑一遍,耗时越来越长;
  • 多个应用和共享包之间缺少统一任务图。

这时 Turborepo 的价值会更明显。

它适合解决:

txt
哪些任务需要先跑
哪些任务可以并行
哪些任务可以复用缓存
哪些 package 受本次修改影响

4. 不要过早引入 Turborepo

如果项目只有一个应用:

txt
my-app/
  package.json
  src/

通常只需要:

bash
pnpm install
pnpm dev
pnpm build

这类场景中,Turborepo 能带来的收益有限,反而会增加 turbo.json、任务产物声明、缓存边界等额外心智负担。

实践记录

场景一:单项目应用

推荐方案:

txt
pnpm

适用条件:

  • 只有一个 Web 应用或 Node.js 服务;
  • 没有共享 package;
  • 构建、测试、lint 时间可接受;
  • CI 没有明显性能瓶颈。

示例命令:

bash
pnpm install
pnpm dev
pnpm build
pnpm test

这时的重点是把包管理、锁文件和脚本规范做好,不需要为了“工程化完整”而额外引入 Turborepo。

场景二:小型 monorepo

推荐方案:

txt
pnpm workspace 起步
Turborepo 可选

典型结构:

txt
repo/
  apps/
    web/
  packages/
    ui/
    utils/

先用 pnpm-workspace.yaml 管理 workspace:

yaml
packages:
  - "apps/*"
  - "packages/*"

如果任务规模还小,可以先使用:

bash
pnpm -r build
pnpm -r lint
pnpm -r test

当出现下面的问题时,再引入 Turborepo:

  • 构建时间明显变长;
  • 每次 CI 都重复构建未变化的 package;
  • package 之间依赖顺序开始复杂;
  • 多个 app 共享多个内部 package;
  • 团队希望复用本地或远程缓存。

场景三:中大型 monorepo

推荐方案:

txt
pnpm workspace + Turborepo

典型结构:

txt
repo/
  apps/
    web/
    admin/
    docs/
  packages/
    ui/
    auth/
    api-client/
    eslint-config/
    tsconfig/

这类项目中,pnpm 和 Turborepo 的职责可以这样划分:

职责工具
安装依赖pnpm
管理 workspacepnpm
链接内部 packagepnpm
执行 build/test/lintTurborepo
处理任务依赖顺序Turborepo
本地缓存和远程缓存Turborepo
CI 增量构建Turborepo

根目录 package.json 可以这样组织:

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 + 多 packagespnpm workspace + Turborepo
CI 构建时间明显变长引入 Turborepo
需要任务缓存和远程缓存引入 Turborepo
需要更强项目图、代码生成、插件体系可进一步评估 Nx

结论

pnpmTurborepo 的区别可以压缩成一句话:

txt
pnpm 是包管理器,Turborepo 是任务编排和缓存工具。

实际选型建议是:

txt
单项目: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 工作流能力。

Last updated:

Powered by VitePress. Written with AI, reviewed by human.