安装
bash
pnpm add turbo
配置
新建turbo.json,在里面配置task任务后,你就可以在package.json中使用turbo <task>
进行任务了,可根据场景配置构建缓存、依赖分析等。
建议结合pnpm-workspace使用
如果要统一相同的依赖可以结合pncat进行,不过得pnpm9以上才行
Turborepo 任务配置笔记
一、核心认知
- 作用:通过 turbo.json 和包图定义任务执行规则,支持并行处理,比传统串行命令(如 yarn workspaces run lint && test && build)更高效
- 核心命令:turbo run [任务名](执行已配置任务)
二、基础准备
1. 关键文件
- 配置入口:根目录 turbo.json(注册任务的核心文件)
- 关联文件:package.json(存储脚本,任务名需与脚本名一致)、apps(应用目录)、packages(内部包目录)
2. 初始化方式
场景 | 操作方式 |
---|---|
全新仓库 | 使用 create-turbo 创建,直接编辑 turbo.json |
现有仓库 | 手动在根目录创建 turbo.json |
三、核心配置:任务定义
1. 基础结构
plain
{
"tasks": {
"任务名": {
"dependsOn": [], // 任务依赖(控制执行顺序)
"outputs": [], // 缓存输出文件/目录
"inputs": [], // 影响缓存哈希的输入文件
"cache": true // 是否缓存(默认true)
}
}
}
⚠️ 注意:仅定义空任务(如 "build": {})会导致并行执行无缓存,易出错,需补充关键配置
2. 任务顺序控制(dependsOn)
(1)依赖「依赖项」的任务(^ 微语法)
- 作用:从依赖图底部执行,确保依赖包先完成任务(如库先 build,应用再 build)
- 示例:
plain
{
"tasks": {
"build": { "dependsOn": ["^build"] } // ui库build → 应用build
}
}
(2)依赖「同一包」的任务
- 作用:同一包内任务按顺序执行(如先 build 再 test)
- 示例:
plain
{
"tasks": {
"test": { "dependsOn": ["build"] } // 同一包:build → test
}
}
(3)依赖「特定包的特定任务」
- 格式:"包名#任务名",支持限定特定包的依赖
- 示例:
- 所有 lint 依赖 utils 的 build:"lint":
- web 包 lint 依赖 utils 的 build:"web#lint":
(4)无依赖任务
- 场景:无需其他任务前置(如 Markdown 拼写检查)
- 配置:省略 dependsOn 或设为空数组
- 示例:"spell-check":
3. 缓存输出配置(outputs)
- 作用:指定任务成功后需缓存的文件 / 目录,未配置则不缓存
- 规则:Glob 模式(相对包路径),支持排除(! 前缀)
- 示例(Next.js 项目):
plain
{
"tasks": {
"build": { "outputs": [".next/**", "!.next/cache/**"] }
}
}
4. 缓存输入配置(inputs)
- 作用:定义影响缓存哈希的文件,默认包含包内 Git 跟踪文件
- 两种配置方式:
配置类型 | 操作方式 | 示例 |
---|---|---|
完全自定义 | 直接指定文件 / Glob,覆盖默认 | "spell-check": |
微调默认 | 用 $TURBO_DEFAULT$ 保留默认,补充排除 | "build": |
5. 根任务注册
- 作用:运行工作区根目录 package.json 中的脚本
- 格式:"//#根任务名": {}
- 示例:
plain
{
"tasks": {
"lint": { "dependsOn": ["^lint"] },
"//#lint:root": {} // 注册根目录 lint:root 脚本
}
}
- 执行命令:turbo run lint:root(单独运行)、turbo run lint lint:root(同时运行所有 lint)
- 适用场景:根目录代码 lint / 格式化、Turbo 迁移过渡、无包范围的脚本
四、高级用例
1. 包级配置
- 操作:在包内创建 turbo.json,自定义该包任务行为
- 优势:大型 monorepo 中,各团队独立控制自己包的任务,不影响全局
2. 长时任务(with 关键字)
- 场景:长时任务需其他任务同时运行(如 web 开发时需 api 服务)
- 配置:需加 persistent: true(任务持续运行)、cache: false(不缓存)
- 示例:
plain
// apps/web/turbo.json
{
"tasks": {
"dev": {
"with": ["api#dev"], // 运行 web#dev 时,同时运行 api#dev
"persistent": true,
"cache": false
}
}
}
3. 副作用任务(强制运行)
- 场景:需始终执行的任务(如构建后部署)
- 配置:"cache": false(跳过缓存,每次强制运行)
- 示例:
plain
{
"tasks": {
"deploy": {
"dependsOn": ["^build"], // 依赖所有包的 build
"cache": false
}
}
}
4. 并行依赖任务(Transit Nodes)
- 问题:如 check-types 任务,需兼顾「并行运行」和「感知依赖项更改」
- 解决方案:引入「中转任务」(无实际脚本,仅建立依赖关系)
- 示例:
plain
{
"tasks": {
"transit": { "dependsOn": ["^transit"] }, // 中转任务,建立依赖链
"check-types": { "dependsOn": ["transit"] } // 依赖中转任务,实现并行+正确性
}
}
筛选器 filter
过滤器可筛选任务,仅运行任务图的子集,常见用法如下:
按包筛选 | turbo build --filter=@acme/web ; turbo run web#build ; turbo run web#build docs#lint | 仅为指定包运行任务,也可直接在 CLI 命令中为不同包指定不同任务,无需使用--filter |
按目录筛选 | turbo lint --filter="./packages/utilities/*" | 通过 glob 模式,聚焦目录下相关包的任务 |
包含依赖项筛选 | turbo build --filter=...ui | 为指定包及其依赖项运行任务,适用于验证包更改是否影响依赖项 |
包含依赖筛选 | turbo dev --filter=web... | 将范围限定为指定包及其所依赖的所有包 |
按源代码控制更改筛选 | turbo build --filter=[HEAD^1] (与上一提交比较); turbo build --filter=[main...my-feature] (与主分支比较); turbo build --filter=[a1b2c3d...e4f5g6h] (按 SHA 比较特定提交); turbo build --filter=[your-feature...my-feature] (按分支名比较特定提交) | 仅为受源代码控制更改影响的包运行任务,过滤器需用[] 括起 |
组合过滤器 | turbo build --filter=...ui --filter={./packages/*} --filter=[HEAD^1] | 多个过滤器组合为并集,任务图包含与任一过滤器匹配的任务 |
根据一些.gitignore
的信息,以及turbo.json
的输入配置,计算作为输入文件的实际内容hash,判断是否缓存。
为什么不用时间戳?如果touch了,内容没变化但是缓存就失效了