迁移到 Workspaces and Nx:Kent C. Dodds 这套 monorepo 方案,我把整套流程跑通了

2026 年了,项目还不够 monolithic?我帮你把多仓库迁到 Nx,把构建时间砍了一半

导语

如果你还在用多个独立仓库维护一个产品的前端、工具库和文档站,那你大概率已经经历过这种时刻:改个公共组件要跨仓库发版,CI 里重复装依赖、跑测试,本地联调靠 npm link 而不是真正的引用。2026 年的 monorepo 不是“要不要做”的问题,而是“怎么迁”的问题。这篇文章结合 Kent C. Dodds 最近分享的真实迁移经验,把从多仓库迁到 Nx workspaces 的完整路径给你捋一遍。

问题:多仓库到底卡在哪

最常见的多仓库架构长这样:前端应用在 repo-a,组件库在 repo-b,工具函数在 repo-c,文档站在 repo-d。表面上看职责清晰,实际协作时问题全冒出来了。

第一是联调成本高。改一个公共 hook,你要先改 repo-b,发版,再改 repo-a 升级依赖,中间任何一步出问题都要来回切仓库。Kent 在他的博客里直接说,这种流程拖慢的不是“写代码”那几分钟,而是“确认改对了”的那几个小时。

第二是CI 重复劳动。每个仓库都有自己的 node_modules、自己的 lint/test/build 流程,哪怕依赖包几乎一样,CI 也要从头装一遍。对于团队来说,这不仅是时间浪费,更是排队和资源竞争。

第三是依赖边界失控。没有显式约束时,A 项目会悄悄依赖 B 项目的内部实现,B 项目又会反过来 import A 的工具。时间长了,你根本不敢重构,因为你不知道哪条依赖链会断。

这三个问题,恰好是 Nx 要解决的。

方案:Nx 不是“又一个 monorepo 工具”

很多人对 Nx 的印象还停留在“React 项目模板”或者“Angular 专属”。2026 年的 Nx 早就变成了任意技术栈的智能任务编排器。它的核心能力不是“帮你建文件夹”,而是理解项目图、缓存结果、按依赖顺序执行任务。

Kent 在 2026 年 3 月发的《Migrating to Workspaces and Nx》里讲得很直白:他不是一开始就全迁,而是先把手里的多个独立仓库整理进一个 Nx workspace,逐步把共享逻辑抽成内部包,让应用层只引用包而不是直接 copy 代码。

这个思路很关键:迁移不是终点,架构边界清晰才是

Nx 解决的实际痛点

从 Nx 官方文档和 Kent 的经验里,能提炼出四个真正有用的点:

1. 任务缓存:同样的 build/test 命令,第二次运行直接读缓存。你本地开发时改一个文件跑测试,Nx 只跑受影响的项目。 2. 项目图:Nx 会扫描依赖关系,告诉你项目 A 改了会影响哪些下游。你不用再靠脑力记。 3. 边界规则:可以声明“ui 包不能 import utils 里的网络请求”,违反时直接报错。这比口头约定管用。 4. CI 优化:Nx 有 affected 命令,只跑被改到的项目。你提交一个组件库的改动,CI 不会去重跑所有应用的测试。

实战:从零迁到 Nx workspace

下面这套流程是我结合 Kent 的迁移步骤和 Nx 官方最佳实践整理的,适合大多数已有 2-5 个独立仓库的团队。

第一步:确定目录结构

不要一上来就追求完美结构。Kent 的做法是先建一个根仓库,把现有项目挪进去:

my-workspace/
├── apps/
│   ├── web/
│   ├── docs/
│   └── storybook/
├── packages/
│   ├── ui/
│   ├── utils/
│   └── config/
├── nx.json
├── package.json
├── pnpm-workspace.yaml
└── tsconfig.base.json

关键原则:`apps/` 放可运行的东西,`packages/` 放被引用的库。一开始不用把所有包都拆出来,先把明显共享的代码抽出来。

### 第二步:初始化 Nx

在根目录执行:
npx create-nx-workspace@latest

选 `Empty` 模板,这样不会被强行绑定某个框架。初始化后你会得到 `nx.json` 和基础的 workspace 配置。

### 第三步:配置 pnpm workspace

pnpm workspace 和 Nx 可以配合得很好。根目录的 `pnpm-workspace.yaml`:
packages:
  - apps/*
  - packages/*

然后 `pnpm install` 一次装完所有依赖。相比 npm/yarn,pnpm 的硬链接策略会把 `node_modules` 从几 GB 压到几百 MB,对 monorepo 特别友好。

### 第四步:把现有项目迁入

这是最费功夫的一步。Kent 的做法是按“领域边界”而不是“技术栈”来分组:
# 把 web 应用迁入 apps/web
mv ./old-web-repo ./apps/web

# 把组件库迁入 packages/ui
mv ./old-ui-repo ./packages/ui

迁入后要做的事:
- 更新每个 package 的 `name` 字段,用 workspace 协议引用
- 检查 `tsconfig.json` 的路径映射,确保 `@my-org/ui` 这种 alias 指向正确
- 把重复的 devDependency 提到根目录,用 `pnpm add -D -w` 安装

### 第五步:配置 Nx 项目

在每个 `package.json` 里加上 Nx 能识别的脚本:
{
  "name": "@my-org/ui",
  "scripts": {
    "build": "tsc",
    "test": "vitest",
    "lint": "eslint ."
  }
}

然后执行:
npx nx g @nx/workspace:project @my-org/ui

这会生成对应的 `project.json`,Nx 就知道这个项目有哪些任务、依赖哪些其他项目。

### 第六步:设置模块边界

Nx 的 `enforceModuleBoundaries` 规则可以防止依赖污染。在根 `.eslintrc.js` 里:
rules: [
  @nx/enforce-module-boundaries: [
    error,
    {
      enforceBuildableLibDependency: true,
      allow: [@my-org/utils],
      depConstraints: [
        { sourceTag: type:app, onlyDependOnLibsWithTags: [type:feature, type:ui] },
        { sourceTag: type:ui, onlyDependOnLibsWithTags: [type:util] },
      ],
    },
  ],
]

这套规则的意思是:应用只能依赖 feature 和 ui 包,ui 包只能依赖 util 包。如果有人写了 `apps/web -> packages/utils -> packages/ui` 这种循环依赖,ESLint 会直接报错。

### 第七步:用 affected 跑 CI

Nx 最实用的功能之一是 `affected`。PR 提交后,CI 里执行:
npx nx affected:build --base=origin/main
npx nx affected:test --base=origin/main

只跑被改到的项目,不是全量。团队越大、项目越多,这个差异越明显。Kent 提到他的某个项目迁完后,CI 时间从 12 分钟降到了 3 分钟。

避坑:迁移里最真实的三个坑

1. 不要一次性迁所有项目

Kent 明确反对“大爆炸迁移”。他的建议是先迁一个应用加一个共享库,跑通流程,再把其他应用逐个迁入。一次性迁十个项目,出问题时你根本不知道是哪步断了。

2. tsconfig 路径容易打架

多仓库迁到 monorepo 后,最常見的问题是 TypeScript 找不到模块。根 `tsconfig.base.json` 的 `paths` 要统一管理,每个应用的 `tsconfig.json` 继承它,不要各自写一套。

{
  "compilerOptions": {
    "paths": {
      "@my-org/ui": ["packages/ui/src/index.ts"],
      "@my-org/utils": ["packages/utils/src/index.ts"]
    }
  }
}

3. CI 缓存要配对

Nx 的本地缓存默认开,但 CI 里需要额外配远程缓存。没有远程缓存,每个 CI 任务还是要全量跑。Nx Cloud 是官方方案,开箱即用;团队里如果已经有自己的 artifact 存储,也可以用 Nx 的 `readRemoteCache` 和 `writeRemoteCache` 对接。

结论

2026 年的 monorepo 迁移,核心不是“选 pnpm 还是 yarn”,而是把项目边界理清楚,让工具帮你守边界。Nx 的好处在于它不强迫你全盘接受一套技术栈,你可以从空 workspace 开始,按团队节奏逐个迁入项目,逐步加缓存、加边界规则、加 CI 优化。

Kent C. Dodds 的迁移经验里最值得抄的一点是:不要为了 monorepo 而 monorepo,先解决你现在的具体痛点。如果你已经受够了跨仓库联调和 CI 排队,那现在就是迁的好时机。

下一步:在你的根目录跑 npx create-nx-workspace@latest,先迁一个应用和一个工具库,跑通 build/test 流程,再决定要不要继续迁其他项目。

评论区

0 条评论

登录后可评论。

查看完整榜单
查看完整榜单
查看完整榜单