Monorepo不是简单合并代码,而是需配套工具链、目录约定和协作规则的工程体系;必须统一子项目存放路径(如packages/或apps/+libs/)、每个子包含独立带scope的package.json、根目录声明workspaces字段、避免嵌套过深,并规范CI、链接、增量构建等实践。

Monorepo 不是“把所有代码扔进一个仓库”就完事——它是一套需要配套工具链、目录约定和协作规则的工程体系。 直接用 git init 建个空仓,再把几个项目文件夹拖进去,后续一定会遇到依赖混乱、构建失败、CI 卡死、权限失控等问题。
怎么组织目录结构才不会让团队天天改 package.json 路径?
目录结构不是个人审美问题,它直接影响工具能否自动识别项目边界、是否支持增量构建、是否能复用 lint/test 配置。
- 必须统一前缀或固定层级:比如所有子项目放在
packages/下(packages/frontend、packages/api、packages/utils),或按业务域划分成apps/+libs/两层 - 每个子项目必须有独立
package.json,且name字段要带 scope(如@myorg/frontend),否则 Yarn Workspaces 或 pnpm 无法正确解析链接关系 - 根目录的
package.json必须声明workspaces字段,例如:"workspaces": ["packages/*", "apps/*"];漏写会导致yarn install不自动链接本地包 - 避免嵌套过深(如
packages/a/b/c/project):Turborepo 和 Nx 的变更检测会变慢,CI 中某些缓存策略也可能失效
git subtree 合并旧仓库时为什么总报 fatal: refusing to merge unrelated histories?
这是 Git 默认行为,不是错误。多仓库迁入 Monorepo 时,各旧仓库历史互不关联,Git 拒绝自动合并防止意外覆盖。
- 必须显式加
--allow-unrelated-histories参数,例如:git merge --allow-unrelated-histories project-a/main - 但更推荐用
git subtree add --prefix=packages/project-a <remote> <branch> --squash:它把整个历史压成一次提交,避免根提交冲突,也减少后续git log的噪音 - 如果要用完整历史,优先选
git filter-repo重写路径后再 merge,而不是直接git pull—— 后者会让所有文件路径变成根目录级,破坏原有相对引用 - 执行前务必
git checkout main并确保工作区干净,否则subtree add可能 silently 失败且不报错
为什么 yarn workspaces 装完依赖,子项目里还是找不到本地 @myorg/utils?
这不是链接没生效,而是 Node 模块解析机制没被正确触发——Yarn 的 workspace link 只负责软链接 node_modules,不改运行时行为。
- 确认子项目
package.json的dependencies或devDependencies里写了"@myorg/utils": "workspace:^"(不是"*"或具体版本号) - 检查根目录
yarn.lock是否生成了resolved指向file:../utils的条目;如果没有,说明yarn install根本没识别到 workspace - VS Code 编辑器可能缓存旧的类型定义,重启 TS Server(
Ctrl+Shift+P → TypeScript: Restart TS server) - 若用 ESM,注意
type: "module"会影响require.resolve行为,此时建议在子项目里加"exports"字段明确导出入口
CI 构建越来越慢,是不是 Monorepo 天然就慢?
不是。慢是因为没启用增量构建,或者每次都在全量装依赖。
- 禁止在 CI 中执行
yarn install全量安装:改用yarn install --immutable --check-cache,配合yarn cache持久化,跳过已存在模块的解压 - TurboRepo 是目前最轻量的增量方案,只要在
turbo.json里配好pipeline依赖图,它就能根据 git diff 自动跳过未改动的子项目 - 避免在根目录跑
yarn test:应按需触发子项目命令,例如turbo run test --filter=frontend...,否则会启动所有项目的测试进程,内存爆满 - 大仓库克隆慢?不是 Git 问题,是 CI runner 没开
git clone --depth=1或没启用 Gitaly pack-objects 缓存——这点在极狐 GitLab 上尤其关键
真正卡住团队的,往往不是“怎么建 Monorepo”,而是“谁来维护 workspace 协议升级”“如何让新成员不手动生成 node_modules”“当 CI 报 Cannot find module '@myorg/shared' 时,第一反应是查路径还是查 turbo 缓存”。这些细节不固化成脚本和文档,Monorepo 就只是个更大的 MultiRepo。


















