构建缓存和远程缓存
要点
- 前面一篇已经把 Turborepo 真正接手的三层事情拆开了:任务编排、依赖关系和缓存
- 如果只看表面,缓存像是在解决“速度”问题
- 最容易先理解的一层,就是本地缓存
- 到了这里,大家最自然会问的通常就是
- 前面已经在 build 任务里写过一次 outputs,但到这一篇,它的重要性会更明显
内容
1. 同样一条 build 命令,第二次为什么会快很多
前面一篇已经把 Turborepo 真正接手的三层事情拆开了:任务编排、依赖关系和缓存。
到了这里,最值得单独掰开讲的就是缓存。
因为它是最容易让人先感受到变化的一层。
第一次执行 build 时,仓库往往还会老老实实把相关任务跑一遍。可等你第二次再执行,速度常常会快很多。有时候甚至会明显感觉到,有些任务像是“几乎没跑”就过去了。
这里背后不是某种魔法,而是缓存开始真正起作用了。
2. 缓存先解决的,不是省几秒,而是别把已经确认过的结果再算一遍
如果只看表面,缓存像是在解决“速度”问题。
但把这件事往里看一层,它真正解决的是重复计算。
一次 build 已经跑过,相关输入也没有变化,那这次再执行同样的任务,本来就没有必要再把同一套结果重新算一遍。
所以缓存不是一种临时提速技巧,而是在告诉任务系统:
这个任务的结果已经被确认过了,如果输入还是同一套,那就直接复用,不要重复执行。
这也是为什么前面那篇一直在强调,缓存不是“跳过执行”的小把戏,而是一种正式保存任务结果的机制。
3. 本地缓存最先带来的,是你自己的下一次执行变快了
最容易先理解的一层,就是本地缓存。
也就是你在自己机器上跑过一次任务以后,结果先保存在本地。后面如果同一个任务再次执行,而且输入条件没有变,本地那份结果就可以直接拿出来复用。
这个场景通常最容易发生在:
// index.bash
pnpm build第一次执行时,相关包里的 build 还是会真的跑。等第二次再执行,如果你中间没有改动会影响这个任务输入的内容,Turborepo 就更可能直接命中本地缓存。
所以本地缓存最直观的价值,其实很朴素:让你自己在同一台机器上,不要反复为同一份结果付第二次代价。
4. 什么东西会影响缓存命中
到了这里,大家最自然会问的通常就是:
那到底什么算“还是同一套输入”。
把这件事讲得太底层会变得很绕,但站在日常开发的角度,先记住一层就够了:
只要任务依赖的内容变了,缓存就应该失效。
最常见的几类就是:
- 任务对应的源码变了
- 相关依赖变了
- 任务命令本身变了
- 环境变量或配置发生了会影响结果的变化
- 声明的输出范围和实际产物关系对不上
所以缓存命不命中,不是看你“有没有手动清缓存”,而是看这次任务是不是还在面对同一套输入。
换句话说,缓存不是在赌运气,它靠的是输入和结果之间的对应关系。
5. 为什么 outputs 这一层一定要说清楚
前面已经在 build 任务里写过一次 outputs,但到这一篇,它的重要性会更明显。
// turbo.json
{
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**", ".next/**", "!.next/cache/**"]
}
}
}这里最关键的不是语法,而是含义。
outputs 说白了,就是把这个任务真正产出的结果范围告诉 Turborepo。它知道结果在哪,后面缓存命中时,才知道该复用什么;任务重新执行时,也才知道哪些产物属于这次任务的一部分。
如果这一层写得太宽、太窄,或者干脆没写清楚,后面最容易出现的就不是“缓存失灵”,而是你自己也说不清这个任务到底产出了什么。
所以很多缓存问题,表面上看像命中率不对,往里追其实常常是 outputs 没有把边界讲清楚。
6. 为什么远程缓存会继续把这件事往前推一步
本地缓存解决的是“同一个人、同一台机器、下一次执行别再重复算”。
但团队一旦开始一起协作,这件事很快就会再往前走一步。
你本地已经算过一次结果,同事那边却还是得从头再跑;CI 里明明也只是同一套输入,又重新花了一遍时间。这时候,本地缓存就开始不够用了。
远程缓存要解决的,就是这一层。
它不是把缓存逻辑改成另一套,而是把原来只留在你机器上的那份任务结果,放到团队也能共同复用的地方。这样一来:
你本地已经算过的结果,别人在同样输入下也有机会直接复用。
CI 已经确认过的构建结果,本地也不一定要再重算一遍。
整个团队开始共享同一套任务结果,而不是每个人都各算各的。
所以远程缓存不是一个额外附加的高级功能,它更像是把“结果复用”这件事从个人扩展到了团队。
7. 真正好用的状态,不是缓存开了,而是大家开始信任它
缓存这件事,真正开始变顺手,往往不是你知道了多少配置,而是团队开始慢慢信任它。
也就是说,大家都知道:
这个任务如果命中缓存,不是“碰巧没跑”,而是前面已经有人或有一台机器把同样输入的结果算过了。
如果这次重新执行了,也不是系统抽风,而是输入确实变了。
一旦 outputs、任务边界和环境约束都摆对了,缓存就是这套任务系统正常的一部分。
到了这一步,仓库里最明显的变化就不再只是“第二次 build 更快”,而是团队开始从心里接受:同样的结果,不需要被每个人再算一遍。
下一篇就接着往下看:当仓库和任务都变多以后,monorepo 最容易在哪些地方开始长歪。
8. 总结
本地缓存让同一台机器上的重复执行不再反复重算,远程缓存则把这件事继续往前推到了团队协作里。
所以这一篇真正想讲清楚的,不是“缓存怎么开”,而是另一件更根本的事:只要输入没变,已经确认过的任务结果,本来就不该再被重复计算。