Turborepo 里同时有营销站、文档站、Web 应用,2026 年是常态。Vercel 支持得很好,但前提是根目录、ignored builds 和共享包都得配对。配错了要么每次 push 都重新构建所有项目(慢且贵),要么压根找不到共享 UI 库。
问题背景
一个 Vercel project 部署一个 app。N 个 app 的 monorepo 就开 N 个 project,每个指向不同子目录。Turbo 负责缓存构建、跳过没受影响的 app。摩擦在边界上:告诉 Vercel 每个 app 在哪、怎么 build、什么时候跳过。
判断标准
- 给一个 app 推个小改动,三个 project 一起重建。
- Vercel 找不到
@workspace/ui,build log 报 “Module not found”。 - Vercel 用了仓库根目录的 build 命令,结果部了错的 app。
- 本地
turbo.json能用,Vercel 上缓存完全没生效。
快速结论
每个 app 放 apps/ 下,一个 app 开一个 Vercel project,Root Directory 设到 apps/<name>,build 用 turbo 包一层,加 ignoreCommand 跳过不影响这个 app 的提交。
能跑的项目结构
典型布局:
my-monorepo/
├── apps/
│ ├── www/ → 营销站(Astro)
│ ├── docs/ → 文档站(Next.js)
│ └── app/ → Web 应用(Next.js)
├── packages/
│ ├── ui/ → 共享 React 组件
│ ├── config-tsconfig/ → 共享 tsconfig
│ └── eslint-config/ → 共享 lint
├── turbo.json
├── package.json → workspaces: ["apps/*", "packages/*"]
└── pnpm-workspace.yaml → 用 pnpm 时
每个 apps/<name>/package.json 把 @workspace/ui 当普通依赖声明(pnpm 用 "workspace:*",npm/yarn workspaces 用 "*")。
每个 app 单独配 Vercel
每个 Vercel project 的 Settings → General:
- Root Directory:
apps/www(或apps/docs之类)。这一步最常被跳过,“module not found”绝大多数是它。 - Build Command:
cd ../.. && pnpm turbo run build --filter=www(按需把pnpm换成npm/yarn)。--filter是让 Turbo 只构建这个 app 及其依赖的关键。 - Install Command:留空——Vercel 自动识别 pnpm / npm / yarn,在仓库根目录跑 workspace install。
- Output Directory:框架默认(Astro 是
dist,Next.js 是.next)。 - Node 版本:在仓库根
package.json的engines.node锁定。
用 Ignored Build Step 跳过没受影响的构建
Vercel monorepo 上最省钱的一招。每个 project 的 Settings → Git → Ignored Build Step:
npx turbo-ignore www
把 www 换成 app 名。turbo-ignore 在该 app 的依赖图自上次成功部署以来没变化时退出 0(跳过构建)。所以只改文档站不会再重新部 www。
不用 Turbo 的话,手动版本:
git diff HEAD^ HEAD --quiet ./ ../../packages/ui ../../packages/config-tsconfig
Vercel 在这条命令退出 0 时跳过构建。
共享包不发布也能用
两种模式都行,选一种坚持下去:
- 构建时转译(Next.js)。
next.config.js用transpilePackages:
/** @type {import('next').NextConfig} */
module.exports = {
transpilePackages: ['@workspace/ui', '@workspace/config-tsconfig'],
};
- 预构建包。 每个包有
build脚本输出到dist/,包的package.json把main/types指到构建产物。turbo根据pipeline.build.dependsOn = ["^build"]先构建依赖再构建依赖方。
模式 1 对内容站更简单。模式 2 在包也要发布到 npm 时扩展性更好。
容易踩的坑
- Root Directory 留在
/,奇怪为啥构建的是错的 app——每个 project 都要指到自己子目录。 - build 命令里在根目录直接
npm install,没让 Vercel 识别 workspace——pnpm / yarn workspaces 会坏。 - build 命令里漏了
--filter——Turbo 每次部署构建所有 app,monorepo 等于白搞。 - 不接
turbo-ignore,为文档里改个错字付全量重建的钱。 - 没在根
package.json锁 Node——Vercel 可能在不同 project 上选不同 Node 版本。 - 两个 app 都需要的环境变量只配在一个 project 里——env 是按 Vercel project 单独作用域的。
FAQ
- Vercel monorepo 一定要用 Turbo 吗?: 不用。Vercel 直接支持 npm / yarn / pnpm workspaces。Turbo 是为了缓存和
turbo-ignore。3 个 app 以内通常 workspaces 就够。 - 一个 repo 能不能一个 Vercel project 部多框架?: 不能。一个 project = 一个框架 / 构建。开多个 project。
- 用 Nx 行不行?: 一样:每个 app 设 Root Directory,build 命令跑
nx build <app>,跳过构建用 Nx 的 affected 命令。 - 跨 project 的 preview deploy 怎么用?: 每个 project 在每个 PR 上单独出 preview URL。可以在 PR 模板里列三个 URL,或者用每个 project 的 Vercel 评论。
- 同一 monorepo 里 Vercel 会跨 project 复用
node_modules缓存吗?: 每个 project 有自己的缓存。把所有 project 接同一个 Turbo remote cache 才能跨 project 共享。