Claude Code 与 Nx Workspace:Monorepo 入门实战
用 Claude Code 和 Nx Workspace 设计边界、查看 nx graph、配置 affected CI 与缓存,避免过度 monorepo。
Nx Workspace 不是“大仓库”,而是边界地图
只对 Claude Code 说“帮我做一个 monorepo”,它通常能生成很多文件。但真正麻烦的是几周以后:apps/web 的改动会不会影响 apps/admin?按钮组件应该放在应用里,还是放进共享 UI?每个 PR 都要跑全部测试吗?
Nx Workspace 用 project graph、task graph、affected 命令和缓存来回答这些问题。官方 Nx mental model 解释了 Nx 如何分析项目并只运行必要任务。给初学者的说法是:Nx 是一张依赖地图加一个任务执行器,它先告诉你“谁依赖谁”,再帮你只验证需要验证的范围。
本文把 Claude Code 当作审查者和结对开发助手,而不是无约束的代码生成器。建议同时打开官方文档:create-nx-workspace、workspace generators、affected、CI setup、caching tasks。相关背景可继续读 Claude Code monorepo 管理、pnpm workspace 实战 和 CI/CD 设置。
什么时候值得用 Nx
Nx 很强,但不是每个小项目都需要。一个落地页加一个很小的 API,用普通仓库可能更快。Nx 适合边界和验证成本开始变高的场景。
第一个真实用例是多个应用共享类型或 UI。比如 apps/web、apps/admin、apps/api、libs/contracts、libs/ui 放在一起,API 类型和 UI 基础组件可以在同一个 PR 里更新。
第二个用例是只跑受影响的 CI。内容站、课程页面、后台和 API 如果在同一个仓库里,每次都全量构建会越来越慢。nx affected -t test build 会根据 Git 差异和依赖图选择可能受影响的项目。
第三个用例是让 Claude Code 更安全。把 libs/ui 定义为纯 UI,libs/contracts 定义为 API 类型,libs/config 定义为共享工具配置,apps/* 定义为可部署应用,Claude Code 的改动范围就会收窄。对有广告、联盟链接、咨询表单或付费 CTA 的页面来说,这一点尤其重要。
如果你还说不清当前仓库的边界,先做一次 repo map first pass。Nx 会加速清晰的结构,但不会自动修好混乱的结构。
目标结构
本教程只做一个小型 workspace:两个 React 应用、一个 Node API、一个 UI 库、一个 contracts 库和一个 config 库。
flowchart LR
web["apps/web\nReact + Vite"] --> ui["libs/ui\nUI primitives"]
admin["apps/admin\nReact + Vite"] --> ui
web --> contracts["libs/contracts\nAPI types"]
admin --> contracts
api["apps/api\nNode API"] --> contracts
web --> config["libs/config\nlint/test config"]
api --> config
规则很简单:apps/* 是运行和部署的单位,libs/* 是可复用的部件。库不能 import 应用。UI 库不应该读取 API 环境变量,contracts 库不应该包含 React 组件。
先把边界交给 Claude Code:
请先理解这个 Nx Workspace,不要急着改代码。
规则:
- apps/web 和 apps/admin 是前端应用
- apps/api 是 API
- libs/ui 只放展示型 UI 基础组件
- libs/contracts 放 API 类型和 Zod schema
- libs/config 放共享 ESLint、Vitest、TypeScript 配置
- libs/* 不能 import apps/*
改动前请查看 nx graph。改动后请给出 nx affected 验证命令。
可复制的初始化命令
以下命令假设已经安装 Node.js 20+、Git 和 pnpm。为了可复现,命令不依赖交互式问题。
npx create-nx-workspace@latest acme-nx \
--workspaceType=integrated \
--preset=apps \
--packageManager=pnpm \
--nxCloud=skip \
--interactive=false
cd acme-nx
pnpm nx add @nx/react
pnpm nx add @nx/node
使用 Nx generator 创建项目。generator 会同步更新 Nx 配置、TypeScript path 和项目元数据,比手工建目录更稳定。
pnpm nx g @nx/react:app web \
--directory=apps/web \
--bundler=vite \
--unitTestRunner=vitest \
--e2eTestRunner=playwright \
--style=css
pnpm nx g @nx/react:app admin \
--directory=apps/admin \
--bundler=vite \
--unitTestRunner=vitest \
--e2eTestRunner=none \
--style=css
pnpm nx g @nx/node:app api \
--directory=apps/api \
--unitTestRunner=vitest
pnpm nx g @nx/react:lib ui \
--directory=libs/ui \
--unitTestRunner=vitest
pnpm nx g @nx/js:lib contracts \
--directory=libs/contracts \
--unitTestRunner=vitest
pnpm nx g @nx/js:lib config \
--directory=libs/config \
--unitTestRunner=none
生成后先看图:
pnpm nx graph
pnpm nx show projects
pnpm nx show project web
如果图里的线已经很多,先简化边界,不要继续增加库。
先读 project.json
对初学者来说,project.json 就是“这个项目能运行哪些任务”的清单。
{
"name": "web",
"sourceRoot": "apps/web/src",
"projectType": "application",
"targets": {
"build": {
"executor": "@nx/vite:build",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/web"
}
},
"test": {
"executor": "@nx/vite:test",
"outputs": ["{workspaceRoot}/coverage/apps/web"],
"options": {
"passWithNoTests": true
}
},
"serve": {
"executor": "@nx/vite:dev-server",
"options": {
"buildTarget": "web:build"
}
}
},
"tags": ["scope:app", "type:web"]
}
让 Claude Code 先解释再修改:
请读取 apps/web/project.json,用初学者能懂的方式解释 build、test、serve。
如果需要改动,只提出最小 diff。
不要破坏 outputs、cache 行为和 dependsOn 关系。
outputs 很重要,因为 Nx 用它判断哪些产物可以缓存。路径写错会导致缓存失效,甚至复用旧产物。
把 affected 放进 CI
affected 是 Nx 最实用的部分。它会比较 Git 差异和依赖图,只对可能受影响的项目执行任务。
pnpm nx affected -t lint test build --base=main --head=HEAD
pnpm nx affected:graph --base=main --head=HEAD
GitHub Actions 里要拉完整历史,否则分支比较容易失败。CI 也要通过 nx 运行任务,直接调用 vitest、eslint 或 tsc 会绕过 Nx 的缓存和 affected 逻辑。
name: nx-ci
on:
pull_request:
push:
branches: [main]
jobs:
affected:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm nx affected -t lint test build --base=origin/main --head=HEAD --parallel=3
团队规模变大后可以考虑 Nx Cloud 远程缓存。初学阶段建议先用本地缓存和 affected 证明价值,再决定是否增加远程缓存。
常见失败
第一,创建 libs/shared 后什么都往里放。让 Claude Code 先说明代码属于 UI、contracts、config 还是纯工具函数,再移动。
第二,libs/* import apps/*。这会让依赖方向反过来,库不再可复用。
第三,CI 里直接跑原始工具。优先使用 pnpm nx run-many -t test 或 pnpm nx affected -t test。
第四,第一周就过度设计。没有真实重复之前,不要急着创建一堆 libs/auth、libs/domain、libs/data-access。
第五,不看 graph 就让 Claude Code 修改。把“改动前 pnpm nx graph,改动后 pnpm nx affected -t test build”写进提示词。
变现角度:保护会赚钱的页面
像 ClaudeCodeLab 这样的站点,文章、培训页、后台、咨询表单和 analytics 可能在同一个仓库里。Nx 的价值不只是快,而是能说明收入相关页面是否被影响。libs/cta 改了,graph 能告诉 reviewer 哪些页面需要确认;libs/analytics 改了,affected CI 能提示哪些应用需要冒烟测试。
这对 AdSense 布局、联盟链接、结账 CTA 和咨询表单都很有用。团队想标准化 Claude Code 和 monorepo 工作流,可以查看 ClaudeCodeLab training。个人练习时,先运行本文命令并保存一张 nx graph 截图,再继续增加 package。
实际验证结果
我以 Masa 的方式验证时,先只创建 web、admin、api、ui、contracts。修改 libs/contracts 的一个类型后,pnpm nx affected -t test build --base=main --head=HEAD 只选择依赖 contracts 的应用。只修改 libs/ui 时,API build 没有进入范围。关键不是背 Nx 参数,而是先让边界和 graph 可见,再让 Claude Code 写代码,改动就更容易审查。
总结
Nx Workspace 不是为了把仓库变大,而是为了让依赖可见、只验证受影响的项目,并给 Claude Code 明确边界。
从小结构开始,读懂 project.json,查看 nx graph,把 nx affected 放进 CI。做到这里,Claude Code 就不只是生成文件,而是能帮助维护 monorepo 结构。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
从Obsidian到CLAUDE.md的Claude Code流程:不再反复解释上下文
把 Obsidian 工作笔记整理成 CLAUDE.md 运行说明,让 Claude Code 每次都带着正确上下文开始。
Claude Code 收入 CTA 路由:从文章分流到 PDF、Gumroad 与咨询
用 Claude Code 按读者意图把文章流量分到免费 PDF、Gumroad 教材或咨询入口。
Claude Code 团队交接规则: 把审查证据、权限、回滚和收入路径一起交付
面向团队的 Claude Code 交接格式: 证据、权限、回滚、免费 PDF、Gumroad 与咨询路径都要可审查。