用 Monorepo(单仓多包) ,本质原因就一句话:
👉 当多个项目/包强相关、需要协同演进时,Monorepo 的综合成本更低。
Monorepo 解决的不是"代码放哪",而是:
- 依赖一致性
- 跨项目协作成本
- 工具链统一
- 原子化变更
一、不用 Monorepo 会遇到什么问题(真实痛点)
假设一个典型大厂前端结构(Multi-repo):
app-web(主站)admin-web(后台)ui-components(组件库)utils(公共工具)eslint-config / webpack-config
1️⃣ 公共包升级是灾难
比如你改了 ui-components:
-
发布 npm 包
-
修改版本号
-
到 5 个仓库里:
- 升级依赖
- 跑测试
- 修兼容问题
-
任意一个没升级 → 线上 bug
👉 版本地狱(dependency hell)
2️⃣ 跨仓库改动没法保证原子性
你想做一个需求:
组件库新增一个 Button 属性,同时业务使用它
Multi-repo 下你只能:
- 提 PR1:组件库
- 发包
- 提 PR2:业务仓库
中间任何一步失败,整体功能就是不完整的
3️⃣ 本地开发体验差
- 本地
npm link/yarn link - 热更新失效
- webpack / Vite 配置不一致
- symlink 引发奇怪 bug
二、Monorepo 是怎么解决这些问题的
🌳 Monorepo 的核心思想
把"逻辑上属于一个产品体系的代码"放在一个仓库里
典型结构(大厂真实结构):
txt
repo/
├─ apps/
│ ├─ web/
│ ├─ admin/
├─ packages/
│ ├─ ui/
│ ├─ utils/
│ ├─ eslint-config/
│ ├─ webpack-config/
├─ pnpm-workspace.yaml
三、Monorepo 优势
1️⃣ 依赖是"源码级"的,不是"版本级"的
json
"@company/ui": "workspace:*"
✅ 改组件 → 业务立即生效
✅ 不需要发包
✅ 不存在版本不一致
对 webpack 来说,这意味着:
- loader / plugin 配置 天然统一
- 不会出现多个 babel / webpack 实例
2️⃣ 跨包改动是「一次提交完成」
txt
git commit -m "feat: Button 支持 loading"
这个 commit 里可以同时:
- 改
ui - 改
web - 改测试
👉 原子性(Atomic Change)
3️⃣ 工具链统一(webpack / eslint / tsconfig)
在 Monorepo 中:
txt
packages/webpack-config/
packages/eslint-config/
所有项目:
js
module.exports = require('@company/webpack-config')
收益:
- 新项目 0 成本接入
- 构建问题一次修,全仓生效
- webpack 升级不再是灾难
4️⃣ 构建 & CI 性能反而更好(不是更差)
配合工具:
- pnpm workspace
- Nx / Turborepo
- changesets
可以做到:
- 只 build 受影响的包
- CI 时间从 40min → 10min
- webpack cache 命中率极高
四、结合 webpack,说一个非常"真实"的例子
Multi-repo 下的噩梦
ui用 webpack 5.88web用 webpack 5.64babel-loader版本不同- 同一个组件在不同项目行为不一致
Monorepo 下
txt
packages/webpack-config
└─ webpack.base.js
- 单一 webpack 版本
- 单一 loader/plugin 版本
- Module Federation / DLL / cache 策略统一
👉 线上问题减少一个数量级
五、Monorepo 适合谁?
✅ 非常适合
- 多个前端项目
- 组件库 + 业务
- 多人协作
- 需要长期维护
❌ 不适合
- 单一项目
- 快速验证 demo
- 一次性活动页