Nx Monorepo 工作区模式实战:nx.json 任务编排、模块边界与 Affected CI 优化全解
【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity项目地址: https://gitcode.com/GitHub_Trending/agents24/agents
本指南以plugins/developer-essentials/skills/nx-workspace-patterns技能包为核心,系统讲解 Nx monorepo 的完整配置模板与落地实践:从nx.json全局任务编排、project.json单项目目标定义,到基于 tag 的模块边界强制约束、自定义 generator 的代码生成,以及利用 affected 命令与远程缓存优化 CI 的全套方案。读完本文,你将能够从零搭建一套具备缓存、增量构建、依赖约束与 CI 加速能力的生产级 Nx 工作区,并理解每一段配置背后的执行原理。
技能定位:这份文档在仓库中的作用
在 developer-essentials 插件中,nx-workspace-patterns技能(SKILL.md)面向"设置 Nx 工作区、配置项目边界、优化构建缓存、落地 affected 命令"等场景,其导航层给出了架构图与最佳实践,而 references/details.md 则承载了 6 套可直接复制的配置模板与常用命令清单——这正是本文的主体骨架。
它与同目录下的 monorepo-management 技能互补:后者覆盖 pnpm workspaces、Turborepo、共享配置与发布流程,是"多工具横向视角";前者则是 Nx 专属的"纵深配置手册"。配套的 monorepo-architect Agent 提供了 8 步工作流(评估代码库 → 选型 → 设计结构 → 缓存策略 → affected 检测 → 任务管线 → 远程缓存 → 文档化约定),本指南中的模板即服务于该工作流的落地执行。
Nx 工作区架构与库类型划分
标准目录结构
从 SKILL.md 可以看出,一个典型 Nx 工作区遵循以下结构:
workspace/ ├── apps/ # Deployable applications │ ├── web/ │ └── api/ ├── libs/ # Shared libraries │ ├── shared/ │ │ ├── ui/ │ │ └── utils/ │ └── feature/ │ ├── auth/ │ └── dashboard/ ├── tools/ # Custom executors/generators ├── nx.json # Nx configuration └── workspace.json # Project configurationapps/:可部署的应用入口(web、api 等),是依赖图的"叶子消费端";libs/:按职责拆分的共享库,是复用的核心单元;tools/:存放自定义 executor(执行器)与 generator(代码生成器);nx.json:工作区级配置,本文 Template 1 详解;project.json:单项目级配置,本文 Template 2 详解。
库的职责分层
| 类型 | 用途 | 示例 |
|---|---|---|
| feature | 智能组件、业务逻辑 | feature-auth |
| ui | 展示型组件 | ui-buttons |
| data-access | API 调用、状态管理 | data-access-users |
| util | 纯函数、工具方法 | util-formatting |
| shell | 应用引导装配 | shell-web |
这套分层是后面"模块边界规则"(Template 3)得以成立的前提——每一层只允许依赖其下层,从而保证依赖图无环、依赖方向可控。
Template 1:nx.json 全局配置逐字段解析
nx.json是 Nx 工作区的"大脑",统一定义任务缓存、输入指纹与依赖顺序。模板全文如下:
{ "$schema": "./node_modules/nx/schemas/nx-schema.json", "npmScope": "myorg", "affected": { "defaultBase": "main" }, "tasksRunnerOptions": { "default": { "runner": "nx/tasks-runners/default", "options": { "cacheableOperations": [ "build", "lint", "test", "e2e", "build-storybook" ], "parallel": 3 } } }, "targetDefaults": { "build": { "dependsOn": ["^build"], "inputs": ["production", "^production"], "cache": true }, "test": { "inputs": ["default", "^production", "{workspaceRoot}/jest.preset.js"], "cache": true }, "lint": { "inputs": ["default", "{workspaceRoot}/.eslintrc.json"], "cache": true }, "e2e": { "inputs": ["default", "^production"], "cache": true } }, "namedInputs": { "default": ["{projectRoot}/**/*", "sharedGlobals"], "production": [ "default", "!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)", "!{projectRoot}/tsconfig.spec.json", "!{projectRoot}/jest.config.[jt]s", "!{projectRoot}/.eslintrc.json" ], "sharedGlobals": [ "{workspaceRoot}/babel.config.json", "{workspaceRoot}/tsconfig.base.json" ] }, "generators": { "@nx/react": { "application": { "style": "css", "linter": "eslint", "bundler": "webpack" }, "library": { "style": "css", "linter": "eslint" }, "component": { "style": "css" } } } }各字段的核心作用
npmScope:为工作区内的包提供统一前缀(如@myorg/web),配合 TS 路径映射使用,保证跨项目导入路径一致。affected.defaultBase:指定 affected 命令的默认对比基线分支(main)。执行nx affected -t test时,Nx 会比较当前 HEAD 与defaultBase之间的变更,只运行受影响的项目的任务。这也是 CI 中"只测改动的"这一核心加速手段的默认锚点。tasksRunnerOptions.default:任务执行器的配置。runner: "nx/tasks-runners/default"使用默认执行器(本地缓存 + 进程并行);cacheableOperations声明哪些操作可被缓存。注意e2e、build-storybook也被列入了缓存集合——只要输入指纹未变,Nx 会直接重放上次的产物,而不是重新执行;parallel: 3控制并行执行的任务数,CI 上可结合机器资源调大(模板 5 的 CI 中--parallel=3即与此呼应)。
targetDefaults:为所有项目的同名 target 提供默认值,避免在每个project.json中重复声明。dependsOn: ["^build"]:语义为"先构建我依赖的所有项目(^表示上游依赖)",从而保证跨项目拓扑排序;inputs:定义该任务的缓存指纹由哪些文件决定(详见namedInputs);cache: true:显式开启该 target 的缓存。
namedInputs:命名一组"文件集合",供inputs引用,是整个缓存命中率的关键设计:default:{projectRoot}/**/*(项目全部文件)加上sharedGlobals;production:在default基础上,用!排除测试文件、快照、tsconfig.spec.json、jest.config与.eslintrc.json——也就是说,仅修改测试相关文件不会使build缓存失效;sharedGlobals:把babel.config.json、tsconfig.base.json这类"改一处影响全局"的根级文件提升为全局输入,任何一处变化都会正确触发下游重算。
generators:为生成器预设默认参数(如 React 应用默认webpack+eslint+css),保证团队内脚手架产物风格统一。
从实现角度看,inputs与namedInputs决定了 Nx 计算"任务哈希"的文件范围:哈希命中即命中缓存,这也是 Nx 相比"全量构建 + 时间戳判断"更快的原因。若把production中排除的测试文件误删,或把依赖文件排除出default,就会造成缓存误命中(改代码不重跑)或误失效(没改代码却重跑),因此namedInputs是缓存调优的第一着力点。
Template 2:project.json 单项目目标定义
每个项目(应用或库)通过project.json声明自己的 targets。以apps/web为例:
// apps/web/project.json { "name": "web", "$schema": "../../node_modules/nx/schemas/project-schema.json", "sourceRoot": "apps/web/src", "projectType": "application", "tags": ["type:app", "scope:web"], "targets": { "build": { "executor": "@nx/webpack:webpack", "outputs": ["{options.outputPath}"], "defaultConfiguration": "production", "options": { "compiler": "babel", "outputPath": "dist/apps/web", "index": "apps/web/src/index.html", "main": "apps/web/src/main.tsx", "tsConfig": "apps/web/tsconfig.app.json", "assets": ["apps/web/src/assets"], "styles": ["apps/web/src/styles.css"] }, "configurations": { "development": { "extractLicenses": false, "optimization": false, "sourceMap": true }, "production": { "optimization": true, "outputHashing": "all", "sourceMap": false, "extractLicenses": true } } }, "serve": { "executor": "@nx/webpack:dev-server", "defaultConfiguration": "development", "options": { "buildTarget": "web:build" }, "configurations": { "development": { "buildTarget": "web:build:development" }, "production": { "buildTarget": "web:build:production" } } }, "test": { "executor": "@nx/jest:jest", "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], "options": { "jestConfig": "apps/web/jest.config.ts", "passWithNoTests": true } }, "lint": { "executor": "@nx/eslint:lint", "outputs": ["{options.outputFile}"], "options": { "lintFilePatterns": ["apps/web/**/*.{ts,tsx,js,jsx}"] } } } }理解 target 的三层结构
executor:指明执行该 target 的工具包,如@nx/webpack:webpack、@nx/jest:jest、@nx/eslint:lint。executor 是 Nx 抽象"构建步骤"的方式——同一个buildtarget 可以在不同项目里换成不同 executor(如 vite 或 rollup),而命令入口nx build web保持一致。outputs:声明任务的产物路径(如dist/apps/web、覆盖率目录)。Nx 缓存的是 outputs 指向的产物,而非源码;缓存重放时也会把产物恢复到该路径。configurations:为同一 target 提供多套参数组合。build默认走production(defaultConfiguration),serve默认走development。生产配置开启optimization、outputHashing: "all"(资源带内容哈希利于缓存)、extractLicenses(抽取许可证文件);开发配置关闭优化、开启 sourceMap,兼顾构建速度与调试体验。tags:项目的"属性标签",如type:app与scope:web。标签本身没有行为,只有被 Template 3 的边界规则引用后才产生约束力。
Template 3:基于 tag 的模块边界规则
模块边界是 Nx 治理依赖混乱的核心武器:它让"哪一层能依赖哪一层"从口头约定变成 CI 中可强制执行的错误。模板位于工作区根.eslintrc.json:
// .eslintrc.json { "root": true, "ignorePatterns": ["**/*"], "plugins": ["@nx"], "overrides": [ { "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], "rules": { "@nx/enforce-module-boundaries": [ "error", { "enforceBuildableLibDependency": true, "allow": [], "depConstraints": [ { "sourceTag": "type:app", "onlyDependOnLibsWithTags": [ "type:feature", "type:ui", "type:data-access", "type:util" ] }, { "sourceTag": "type:feature", "onlyDependOnLibsWithTags": [ "type:ui", "type:data-access", "type:util" ] }, { "sourceTag": "type:ui", "onlyDependOnLibsWithTags": ["type:ui", "type:util"] }, { "sourceTag": "type:data-access", "onlyDependOnLibsWithTags": ["type:data-access", "type:util"] }, { "sourceTag": "type:util", "onlyDependOnLibsWithTags": ["type:util"] }, { "sourceTag": "scope:web", "onlyDependOnLibsWithTags": ["scope:web", "scope:shared"] }, { "sourceTag": "scope:api", "onlyDependOnLibsWithTags": ["scope:api", "scope:shared"] }, { "sourceTag": "scope:shared", "onlyDependOnLibsWithTags": ["scope:shared"] } ] } ] } } ] }规则如何生效
@nx/enforce-module-boundaries规则会读取每个项目project.json中的tags,再按depConstraints检查依赖方向:
- 按职责分层(type:*):
type:app可依赖 feature/ui/data-access/util;type:feature不可再依赖其他 feature(避免业务逻辑互串),只能依赖 ui/data-access/util;type:ui只能依赖 ui 与 util;type:data-access只能依赖>// tools/generators/feature-lib/index.ts import { Tree, formatFiles, generateFiles, joinPathFragments, names, readProjectConfiguration, } from "@nx/devkit"; import { libraryGenerator } from "@nx/react"; interface FeatureLibraryGeneratorSchema { name: string; scope: string; directory?: string; } export default async function featureLibraryGenerator( tree: Tree, options: FeatureLibraryGeneratorSchema, ) { const { name, scope, directory } = options; const projectDirectory = directory ? `${directory}/${name}` : `libs/${scope}/feature-${name}`; // Generate base library await libraryGenerator(tree, { name: `feature-${name}`, directory: projectDirectory, tags: `type:feature,scope:${scope}`, style: "css", skipTsConfig: false, skipFormat: true, unitTestRunner: "jest", linter: "eslint", }); // Add custom files const projectConfig = readProjectConfiguration( tree, `${scope}-feature-${name}`, ); const projectNames = names(name); generateFiles( tree, joinPathFragments(__dirname, "files"), projectConfig.sourceRoot, { ...projectNames, scope, tmpl: "", }, ); await formatFiles(tree); }组合式生成的实现思路
- Schema 接口:
FeatureLibraryGeneratorSchema声明入参name、scope与可选的directory,生成器的参数校验与 CLI 提示都基于它。 - 复用官方生成器:先调用
libraryGenerator(来自@nx/react)生成标准库骨架,同时通过tags: "type:feature,scope:${scope}"自动打上符合 Template 3 约束的标签,保证"生成即合规"。 - 模板文件注入:利用
generateFiles把files/目录下的模板文件(支持__tmpl__后缀与模板插值)注入到projectConfig.sourceRoot;names()工具负责把 kebab-case 名称转换成 camelCase、PascalCase 等变体供模板使用。 - 收尾格式化:
formatFiles(tree)统一运行 prettier,保证生成代码风格一致。
执行时只需
nx g feature-lib --name=auth --scope=web,即可得到带正确目录、标签、测试与 lint 配置的完整 feature 库,避免手写带来的目录漂移。Template 5:用 Affected 命令优化 CI 流水线
CI 是全量构建的重灾区。模板给出的 GitHub Actions 工作流(
.github/workflows/ci.yml)只对"受影响的项目"执行任务:# .github/workflows/ci.yml name: CI on: push: branches: [main] pull_request: branches: [main] env: NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} jobs: main: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - uses: actions/setup-node@v4 with: node-version: 20 cache: "npm" - name: Install dependencies run: npm ci - name: Derive SHAs for affected commands uses: nrwl/nx-set-shas@v4 - name: Run affected lint run: npx nx affected -t lint --parallel=3 - name: Run affected test run: npx nx affected -t test --parallel=3 --configuration=ci - name: Run affected build run: npx nx affected -t build --parallel=3 - name: Run affected e2e run: npx nx affected -t e2e --parallel=1三个关键细节
fetch-depth: 0:affected 命令需要完整的 git 历史来计算"从 base 到 head 改了哪些项目"。浅克隆(默认 fetch 深度 1)会导致 Nx 找不到对比基线。nrwl/nx-set-shas@v4:官方 action 负责从 CI 环境推导出正确的 base/head SHA(PR 场景取目标分支与合并基),并写入环境变量供后续nx affected消费。- 分级并行度:lint/test/build 使用
--parallel=3提升吞吐;e2e 用--parallel=1串行执行——端到端测试常占用浏览器、端口等共享资源,过高的并行度反而会引入不稳定。
受益于 Template 1 的缓存配置与
NX_CLOUD_ACCESS_TOKEN(对应 Template 6 的远程缓存),未受影响的项目的任务在 CI 中直接命中远程缓存,整体流水线时间与改动范围成正比,而非与仓库规模成正比。Template 6:本地缓存之外——Nx Cloud 与自托管 S3 缓存
Nx Cloud 远程缓存
// nx.json with Nx Cloud { "tasksRunnerOptions": { "default": { "runner": "nx-cloud", "options": { "cacheableOperations": ["build", "lint", "test", "e2e"], "accessToken": "your-nx-cloud-token", "parallel": 3, "cacheDirectory": ".nx/cache" } } }, "nxCloudAccessToken": "your-nx-cloud-token" }将 runner 切换为
nx-cloud后,任务结果会上传到云端:本机跑过一次的任务,CI 和同事的机器可以直接拉取产物,实现跨机器、跨环境的缓存共享。cacheDirectory指定本地缓存目录(默认.nx/cache),Token 建议通过环境变量注入而非硬编码。自托管 S3 缓存
// Self-hosted cache with S3 { "tasksRunnerOptions": { "default": { "runner": "@nx-aws-cache/nx-aws-cache", "options": { "cacheableOperations": ["build", "lint", "test"], "awsRegion": "us-east-1", "awsBucket": "my-nx-cache-bucket", "awsProfile": "default" } } } }若不便使用云端服务,可通过社区 runner
@nx-aws-cache/nx-aws-cache将缓存落到自有 S3 桶(awsBucket),用awsRegion与awsProfile控制访问凭据。注意:远程缓存的安全模型基于"输入哈希即缓存键",因此务必保证namedInputs覆盖所有影响产出的文件(含全局配置、环境变量类输入),防止缓存串扰。常用命令速查
以下命令贯穿日常开发与 CI 场景(来自 references/details.md):
# Generate new library nx g @nx/react:lib feature-auth --directory=libs/web --tags=type:feature,scope:web # Run affected tests nx affected -t test --base=main # View dependency graph nx graph # Run specific project nx build web --configuration=production # Reset cache nx reset # Run migrations nx migrate latest nx migrate --run-migrationsnx g @nx/react:lib ...:生成库并直接指定目录与标签,与 Template 4 的自定义 generator 目标一致——生成时打对标签,边界规则才能生效;nx affected -t test --base=main:手动指定基线执行增量测试,等价于 CI 中nx-set-shas推导后的行为;nx graph:打开交互式依赖图面板,快速检查依赖方向与环形依赖;nx build web --configuration=production:按 Template 2 中production配置构建单项目;nx reset:清空本地缓存与 daemon 状态,用于排查缓存异常;nx migrate latest/nx migrate --run-migrations:先分析版本迁移方案,再实际执行迁移脚本,保证 Nx 升级可控。
最佳实践:Do's 与 Don'ts
结合 SKILL.md 的沉淀:
应该做(Do's)
- 标签一致化 + 模块边界强制:
type:*、scope:*标签与enforce-module-boundaries配合,把架构约束写成可执行的错误; - 尽早开启缓存:Template 1 中声明
cacheableOperations与inputs,是 CI 提速收益最显著的单项投资; - 保持库的单一职责:feature/ui/data-access/util 各司其职,避免出现"什么都放"的杂库;
- 统一使用 generator:无论是官方生成器还是 Template 4 的自定义生成器,保证脚手架与标签的一致性;
- 文档化边界:让新成员能从目录与
nx graph中快速理解依赖规则。
不要做(Don'ts)
- 不要制造循环依赖:依赖图必须保持无环,否则缓存、affected 与增量构建都会失去正确性;
- 不要跳过 affected:全量跑测试会让 CI 随仓库膨胀线性变慢,白白浪费 Nx 的核心能力;
- 不要忽视边界:松懈的依赖约束会积累隐性技术债,重构成本随项目规模指数上升;
- 不要过度拆分库:库数量与复杂度要平衡,粒度过细会导致维护与感知成本超过收益。
小结
从
nx.json的输入指纹与任务编排,到project.json的目标分层,再到标签驱动的模块边界、组合式 generator、affected CI 与远程缓存,这套模式构成了一条完整的 Nx 落地链路。对 Agent 与开发者而言,references/details.md 是可随时复用的模板库,SKILL.md 是导航与决策依据,而 monorepo-architect Agent 则提供了从评估到落地的 8 步流程指引。三者配合,即可在任意规模的项目中搭建出"改动越小、验证越快、依赖越清晰"的生产级 Nx 工作区。【免费下载链接】agentsMulti-harness agentic plugin marketplace for Claude Code, Codex, Cursor, OpenCode, GitHub Copilot, and Google Antigravity
项目地址: https://gitcode.com/GitHub_Trending/agents24/agents
- Schema 接口:
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考