SuperAPI AI 工具集

turborepo

部署与运维

查看项目

更新于 10-15 08:17

技能介绍

Turborepo 是一个面向 JavaScript/TypeScript 的 monorepo 构建系统。本条目假设读者已了解 turbo run build 的基本用法,重点讨论架构决策,而非入门操作。

一、采用前的战略评估 资料给出了一组判断信号与建议:1-3 名工程师时建议 polyrepo,因为 monorepo 的额外开销不划算;共享代码低于 20% 建议 polyrepo;共享代码超过 50% 且需要频繁协作时 monorepo 更有吸引力;混合语言(Go/Python/JS)建议 Nx 或 polyrepo,因为 Turborepo 聚焦 JS/TS;所有构建总时长低于 5 分钟时,开销尚不值得;破坏性变更需要改动 3 个以上仓库时 monorepo 更优;服务独立部署时建议 polyrepo。资料给出的盈亏平衡点是:当 3 个以上应用共享 30% 以上代码,且需要频繁协作时,monorepo 才值得。

二、核心规则:任务放在包内,而非根目录 资料称这是 Turborepo 最常见的错误——把任务逻辑写进根 package.json。错误做法是在根脚本里用 cd 串联各应用的构建命令,这会破坏并行化;正确做法是每个包自己拥有构建脚本,根 package.json 只做委托(turbo run build)。原因是 Turborepo 无法并行化顺序执行的 shell 命令,包级任务才能启用任务图并行化。

三、何时拆分一个包 判断路径为:仅被 1 个应用使用则暂不拆分,保留在应用内等待第二个消费者,避免过早抽象;被 2 个以上应用使用则视情况拆分——API 稳定(很少变更)可拆分,每个迭代都在变则暂不拆分,多团队混合归属则暂不拆分(改用导入路径);需要发布到 npm 则必须拆分;CI 构建超过 10 分钟时,按稳定性而非领域拆分,稳定包可缓存,不稳定包总是重建。资料强调的反模式是:为“整洁架构”创建没有消费者的包,每个包都会增加构建、测试和版本管理开销。

四、常见反模式与修复 1)循环依赖:症状是 turbo run build 报“Could not resolve dependency graph”。修复方式是把共享代码抽到第三个包(如 packages/shared);间接循环(A→B→C→A)可用 npx madge --circular --extensions ts,tsx packages/ 检测。 2)包粒度过细:症状是每个功能牵动 5 个以上包、每个迭代 10 次以上版本号变更、pnpm workspace:* 版本管理混乱。修复是按变更频率而非领域分组,例如 packages/ui(组件,频繁变更)、packages/ui-primitives(无头组件,稳定)、packages/icons(生成的 SVG,很少变更)。规则是:包边界等于不同的变更频率,总是一起变更的包应合并为一个。 3)缺失任务依赖:症状是本地测试通过、CI 报“Cannot find module './dist/index.js'”,原因是测试在构建完成前运行,属于竞态。修复是在 turbo.json 中为 test 添加 dependsOn: ["build"],build 使用 dependsOn: ["^build"] 与 outputs: ["dist/"]。^build 表示先构建本包的依赖,build 表示先构建本包。 4)缓存未命中:症状是缓存从不命中、每次全量重建,原因是 inputs glob 过宽,注释变更也会触发重建。修复是收窄 inputs,例如 src//*.{ts,tsx} 并排除测试文件。调试命令包括 turbo run build --dry --graph 可视化任务图,以及 turbo run build --dry=json | jq '.tasks[] | select(.cache.status == "MISS")'。

五、monorepo 与 polyrepo 的决策 新项目判断路径:单团队单产品选 polyrepo(更简单);需要共享 UI 库选 monorepo,以便同时开发库并在消费者中测试;不同语言的微服务选 polyrepo,因为 Turborepo 聚焦 JS/TS;多团队、共享代码、需要原子化变更选 monorepo。资料给出的实用建议是:先 polyrepo,当跨仓库协作的痛苦超过工具成本时再迁移到 monorepo。

六、包边界模式 按稳定性(推荐):packages/core(按季度变更,语义化版本)、packages/features(按周变更,workspace 协议)、packages/utils(按月变更)。按消费者:packages/public-api(外部消费者,严格版本管理)、packages/internal(内部应用,可用 workspace 协议)。按团队:仅在团队很少共享代码时可行,否则会形成孤岛。

七、与替代方案对比 优先 Turborepo 的场景:JS/TS monorepo、使用 pnpm/npm workspaces、需要 Vercel 远程缓存。优先 Nx 的场景:需要项目图可视化、多语言(JS + Python + Go)、需要强约定的项目结构。优先 Rush 的场景:100 个以上包、以发布到 npm 为主要目标、需要幻影依赖检测。

八、错误恢复 缓存从不命中:用 turbo run build --dry=json | jq '.tasks[0].hash' 查看当前哈希,收窄 inputs glob 排除非代码文件,临时在 turbo.json 中设 "cache": false 以便在无缓存压力下调试。循环依赖:用 turbo run build --dry --graph=graph.html 在浏览器中可视化,用 madge 检测间接循环,把公共代码抽到 packages/shared。CI 测试失败但本地通过:用 turbo run test --dry --graph 确认构建先于测试运行,为 test 添加 dependsOn: ["build"],用 turbo run test --force 绕过缓存确认顺序。包粒度过细导致版本管理混乱:用 git log --oneline --since="1 month ago" -- packages/ 统计各包版本变更次数,一起变更 5 次以上的包应合并,规划合并期间可先用 workspace:* 自动关联版本。

九、何时加载完整参考 遇到 3 个以上未知 CLI 标志、需要跨 10 个以上包的高级 --filter 模式、或配置复杂流水线选项时,阅读 references/cli-options.md;为团队搭建远程缓存、调试缓存鉴权错误、或配置自定义存储的自托管缓存时,阅读 references/remote-cache-setup.md。基础架构决策、单次缓存未命中调试、monorepo 采用决策无需加载参考。

官方文档:https://turbo.build/repo/docs