Vercel Monorepo 内容站部署:Turbo + 多 App

从 Turborepo monorepo 把内容站部到 Vercel,大部分是配置不是魔法。本文给你能跑的根目录配置、ignoreCommand 和共享包接线方式。

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 Directoryapps/www(或 apps/docs 之类)。这一步最常被跳过,“module not found”绝大多数是它。
  • Build Commandcd ../.. && 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.jsonengines.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 时跳过构建。

共享包不发布也能用

两种模式都行,选一种坚持下去:

  1. 构建时转译(Next.js)。 next.config.jstranspilePackages
/** @type {import('next').NextConfig} */
module.exports = {
  transpilePackages: ['@workspace/ui', '@workspace/config-tsconfig'],
};
  1. 预构建包。 每个包有 build 脚本输出到 dist/,包的 package.jsonmain / 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 共享。

相关阅读

标签: #独立开发 #Vercel #部署 / 托管 #monorepo #工作流