一、Turbopack冷启动慢的核心问题:缓存没找对地方

很多刚用Turbopack的开发者都会遇到一个糟心的问题:改个项目跑起来要等好几分钟,甚至比Webpack还慢。这背后90%的原因是缓存策略配错了——要么缓存没存对地方,要么没用到正确的缓存资源,导致每次启动都要重新编译所有代码。

先给大家明确什么是冷启动:就是你第一次跑Turbopack(比如刚拉完代码、换了新电脑、缓存被清空)的时候,Turbopack从0开始编译整个项目的过程。如果这个过程慢,说明缓存没有帮上忙,反而拖了后腿。

1.1 缓存的本质:避免重复做无用功

举个最通俗的例子:你做番茄炒蛋,第一次要洗番茄、切番茄、打鸡蛋、炒鸡蛋、炒番茄、装盘,整个流程下来要10分钟。但如果你提前把番茄切好、鸡蛋打好(相当于缓存了中间结果),下次再做只要3分钟——缓存就是帮你存下“已经做好的中间步骤”,下次直接用,不用重复做。

Turbopack的缓存也是这个逻辑:它会把编译好的JS、CSS、图片等资源,还有项目依赖(比如React、Vue这些第三方库)的编译结果存起来,下次启动的时候直接拿过来用,不用重新编译。

二、正确配置缓存的核心逻辑:分两类缓存分别优化

Turbopack的缓存分两大类:项目依赖缓存项目源码缓存。这两类缓存的作用不一样,配置方法也完全不同,很多人就是混着配,导致缓存失效。

2.1 先搞懂两类缓存的区别

我用一个真实的项目场景给大家讲:假设你做一个电商网站,用React写,项目里有自己写的组件(比如商品卡片、购物车),还有用的第三方库(比如React、ReactDOM、antd)。

  • 项目依赖缓存:存的是React、antd这些第三方库的编译结果。这些库的代码很少变,只要版本不变,编译结果就可以一直用。
  • 项目源码缓存:存的是你自己写的组件、页面这些代码的编译结果。这些代码你每天都改,缓存的有效期比较短。

很多人配缓存的时候,要么把两类缓存混存在一个文件夹里,要么只开了其中一类,导致缓存要么用不上,要么更新不及时。

2.2 第一类缓存:项目依赖缓存的配置(最容易被忽略)

项目依赖缓存是冷启动提速的核心,因为第三方库的代码量通常很大(比如antd有几百个组件,编译一次要好几分钟)。如果依赖缓存没配好,每次冷启动都要重新编译所有依赖,速度肯定慢。

2.2.1 依赖缓存的默认问题:存错地方

Turbopack默认会把依赖缓存存在项目根目录的.turbo文件夹里。这个文件夹有个大问题:每次你拉新代码、换分支、或者清项目缓存的时候,都会把.turbo删掉,导致依赖缓存直接没了,下次启动又要重新编译。

举个例子:你今天拉了同事的新分支,执行pnpm dev,发现要等5分钟,一看.turbo文件夹没了——因为同事的分支里没提交.turbo(也不能提交,这个文件夹太大),所以你本地的依赖缓存直接失效了。

2.2.2 正确配置:把依赖缓存存在全局目录

解决方法很简单:把依赖缓存存在电脑的全局目录里,不管你拉什么项目、换什么分支,只要依赖的版本没变,就可以直接用全局的缓存。

这里给大家一个完整的配置示例,用的是pnpm + Turbopack + React技术栈(所有示例都用这个技术栈,不会乱):

首先,在项目根目录新建一个.turbo配置文件(注意是.turbo,不是.turbo.json),内容如下:

{
  "cache": {
    // 把依赖缓存存在全局目录,不管哪个项目都能用
    "global": true,
    // 全局缓存的存储路径(Windows、Mac、Linux都适用)
    "globalCacheDir": "~/.turbo-global-cache"
  }
}

然后,在package.json里的启动命令里加上缓存参数,告诉Turbopack启用全局缓存:

{
  "scripts": {
    "dev": "next dev --turbo --cacheDir=~/.turbo-global-cache"
  }
}

2.2.3 配置后的效果验证

配完之后,你可以做个测试:

  1. 第一次跑pnpm dev,等冷启动完成(假设花了3分钟);
  2. 手动删掉项目根目录的.turbo文件夹;
  3. 再跑一次pnpm dev,你会发现冷启动只花了10秒——因为依赖缓存存在全局目录,没有被删掉,直接用了上次的编译结果。

2.3 第二类缓存:项目源码缓存的配置(平衡速度和正确性)

源码缓存是你自己写的代码的编译结果,这个缓存不能存在全局目录,因为每个项目的源码都不一样,全局存反而会乱。

2.3.1 源码缓存的默认问题:要么太旧要么太新

Turbopack默认的源码缓存有两个坑:

  • 坑1:缓存永远不更新,导致你改了代码,启动的时候还是用旧的编译结果,页面看不到变化;
  • 坑2:缓存更新太频繁,每次改一行代码都要重新编译整个项目,速度慢。

2.3.2 正确配置:给源码缓存加“过期时间”和“变化检测”

解决方法是给源码缓存加两个规则:

  1. 缓存只保留1小时(你自己可以调,比如改成2小时),超过时间就重新编译;
  2. 只有当你改了源码里的文件(比如src文件夹里的代码),才会更新缓存。

还是用刚才的技术栈,在.turbo配置文件里加源码缓存的配置:

{
  "cache": {
    "global": true,
    "globalCacheDir": "~/.turbo-global-cache",
    // 源码缓存的配置
    "local": {
      // 源码缓存的存储路径(项目根目录的.turbo-local)
      "dir": ".turbo-local",
      // 缓存过期时间:1小时(单位:秒,3600秒=1小时)
      "ttl": 3600,
      // 只有当src文件夹里的文件变化时,才会更新缓存
      "watch": ["src/**/*"]
    }
  }
}

然后在package.json的启动命令里加上源码缓存的参数:

{
  "scripts": {
    "dev": "next dev --turbo --cacheDir=~/.turbo-global-cache --localCacheDir=.turbo-local"
  }
}

2.3.3 配置后的效果验证

你可以做个测试:

  1. 第一次跑pnpm dev,等冷启动完成;
  2. src/App.js里的代码,保存后页面会立刻更新(因为源码缓存检测到src文件夹变化,重新编译了这个文件);
  3. 过1小时后再跑pnpm dev,会重新编译所有源码(因为缓存过期了)。

三、容易踩的缓存坑:避开这些错误配置

很多人配缓存的时候,会犯一些低级错误,导致缓存完全没用,这里给大家列几个最常见的坑:

3.1 坑1:把依赖缓存和源码缓存混存在一个文件夹

比如有人在.turbo配置里只写了一个dir,把两类缓存都存在同一个文件夹里,结果:

  • 换分支的时候,依赖缓存被删掉,下次启动又要重新编译;
  • 改源码的时候,依赖缓存被更新,导致依赖的编译结果变了,页面出问题。

正确的做法是两类缓存分开存,像刚才的示例一样,依赖缓存存在全局目录,源码缓存存在项目本地的.turbo-local

3.2 坑2:缓存路径里有中文或特殊字符

比如有人把全局缓存路径写成C:\用户\张三\.turbo-global-cache,或者~/.turbo 缓存,结果Turbopack找不到缓存路径,导致缓存完全没用。

正确的做法是缓存路径只用英文、数字、横杠、斜杠,不要有中文、空格、特殊字符(比如~/.turbo-global-cache是对的)。

3.3 坑3:启动命令里的缓存参数和配置文件不一致

比如配置文件里写的全局缓存路径是~/.turbo-global-cache,但启动命令里写的是--cacheDir=~/.turbo-cache,结果Turbopack找不到缓存,冷启动还是慢。

正确的做法是启动命令里的缓存参数和配置文件里的路径完全一致,最好的方法是把配置写在.turbo文件里,启动命令只加--turbo,这样不会乱。

四、不同场景下的缓存优化方案

不同的项目场景,缓存的配置也不一样,这里给大家列几个常见的场景:

4.1 场景1:个人开发项目(自己用)

个人项目的需求是速度快,所以可以把依赖缓存的过期时间调长(比如7天),源码缓存的过期时间调短(比如30分钟),这样改代码的时候缓存更新快,依赖缓存又能长期用。

配置示例:

{
  "cache": {
    "global": true,
    "globalCacheDir": "~/.turbo-global-cache",
    "local": {
      "dir": ".turbo-local",
      "ttl": 1800, // 源码缓存30分钟过期
      "watch": ["src/**/*"]
    }
  }
}

4.2 场景2:团队开发项目(多人用)

团队项目的需求是一致性,所以要把依赖缓存的路径统一(比如大家都用~/.turbo-global-cache),并且把缓存的配置提交到代码仓库(.turbo文件),这样大家的缓存配置都一样,冷启动速度也一样。

注意:不要把.turbo-local(源码缓存)提交到代码仓库,因为每个开发者的源码不一样,源码缓存是本地用的。

4.3 场景3:CI/CD流水线(自动部署)

CI/CD流水线的需求是速度快,所以要把全局缓存存在流水线的缓存里(比如GitHub Actions的缓存),这样每次流水线跑的时候,都能用上上次的依赖缓存,不用重新编译。

比如GitHub Actions的配置示例:

name: Build
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      # 缓存全局缓存目录
      - uses: actions/cache@v3
        with:
          path: ~/.turbo-global-cache
          key: ${{ runner.os }}-turbo-global-cache-${{ hashFiles('pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-turbo-global-cache-
      - run: pnpm install
      - run: pnpm build

五、缓存配置的优缺点和注意事项

5.1 缓存配置的优点

  1. 冷启动速度提升50%-90%:比如原来冷启动要5分钟,配完缓存只要30秒;
  2. 开发体验变好:改代码的时候页面更新更快,不用等很久;
  3. 团队效率提升:大家的冷启动速度都一样,不会出现有人快有人慢的情况;
  4. CI/CD速度提升:流水线跑的时间变短,部署更快。

5.2 缓存配置的缺点

  1. 可能出现缓存污染:如果缓存里的编译结果有问题,会导致所有用这个缓存的项目都出问题;
  2. 占用磁盘空间:全局缓存会存很多依赖的编译结果,可能占几个G的空间;
  3. 配置稍微复杂:要区分两类缓存,不能混着配。

5.3 注意事项

  1. 定期清理缓存:如果发现页面出问题,或者缓存占的空间太大,可以删掉全局缓存(~/.turbo-global-cache)和本地源码缓存(.turbo-local),重新启动;
  2. 版本更新后要清缓存:如果升级了Turbopack的版本,或者升级了依赖的版本,最好清一下缓存,避免旧缓存和新版本不兼容;
  3. 不要提交缓存文件夹:.turbo(项目依赖缓存的配置文件可以提交,但缓存文件夹本身不能提交)、.turbo-local这些缓存文件夹要加到.gitignore里,避免提交到代码仓库;
  4. 缓存路径不要用相对路径:比如不要把全局缓存路径写成./.turbo-global-cache,要写成绝对路径(比如~/.turbo-global-cache),避免路径找不到。

六、总结

Turbopack冷启动慢的核心原因是缓存策略配错了,只要把依赖缓存和源码缓存分开配置,就能解决90%的问题。具体的步骤总结下来就是:

  1. 把依赖缓存存在全局目录,只要版本不变,就可以一直用;
  2. 把源码缓存存在项目本地,加过期时间和变化检测,平衡速度和正确性;
  3. 避开常见的缓存坑,比如路径不对、配置不一致;
  4. 根据不同的场景调整缓存配置,满足个人、团队、CI/CD的需求。

最后给大家一个完整的.turbo配置文件(适用于个人和团队开发的React项目):

{
  "cache": {
    "global": true,
    "globalCacheDir": "~/.turbo-global-cache",
    "local": {
      "dir": ".turbo-local",
      "ttl": 3600,
      "watch": ["src/**/*"]
    }
  }
}

启动命令:

{
  "scripts": {
    "dev": "next dev --turbo",
    "build": "next build --turbo"
  }
}

只要配好这个,你的Turbopack冷启动速度就会快很多,开发体验也会变好。