🚀 如何用 Monorepo 管理多端项目?一套可落地方案
一、从"架构设计"到"工程落地"
在上一篇中,我们解决了一个核心问题:
text
多端架构应该如何设计?
我们得出的结论是:
text
用分层架构统一逻辑(UI / modules / services)
👉 那这一篇,我们解决另一个更现实的问题:
text
如何把这套架构真正落地?
二、为什么多端项目一定会"失控"?
当你开始同时维护 Web、小程序、App 等多个端时,传统的 Multi-repo(多仓库) 很容易演变成开发灾难:
- ❌ 重复劳动:相同业务逻辑在多个仓库重复实现
- ❌ 同步地狱:接口字段变更,需要手动同步多个项目
- ❌ 维护混乱:修一个 Bug,要改多个仓库
🔥 本质问题:
text
代码没有统一的"抽象与复用边界"
👉 所以你需要一种机制:
text
既能共享代码,又能保持边界清晰
👉 这就是:
text
Monorepo(单仓多包)
三、为什么多端架构必须用 Monorepo?
相比传统 Multi-repo,Monorepo 的优势非常明显:
| 维度 | Multi-repo | Monorepo |
|---|---|---|
| 代码复用 | 复制 / npm 发布 | 本地直接引用 |
| 类型共享 | 手动同步 | 自动同步 |
| 依赖管理 | 各自维护 | 统一管理 |
| 代码变更 | 多仓提交 | 原子提交 |
👉 对多端项目来说,它解决了最核心的问题:
text
让"可复用逻辑"有了统一载体
四、项目结构设计(核心)
这是 Monorepo 成败的关键。
📦 推荐结构(与架构分层一致):
text
my-repo/
├── apps/ # 应用层(各端独立)
│ ├── web/ # Web(Next.js / React)
│ ├── mini/ # 原生小程序
│ └── admin/ # 管理后台
│
├── packages/ # 复用能力层
│ ├── services/ # API 层(OpenAPI)
│ ├── modules/ # 业务逻辑(核心 ⭐)
│ ├── request/ # 请求适配层
│ └── shared/ # 工具函数
│
├── package.json
└── pnpm-workspace.yaml
🔥 核心设计原则:
text
apps = 面向用户(不可复用)
packages = 面向复用(核心资产)
👉 最关键的一点:
text
所有"可复用逻辑",必须进入 packages,而不是 apps
五、从 0 搭建 Monorepo(实操)
1️⃣ 初始化项目
bash
mkdir my-repo && cd my-repo
pnpm init
2️⃣ 配置 workspace
创建 pnpm-workspace.yaml:
yaml
packages:
- "apps/*"
- "packages/*"
3️⃣ 创建目录结构
bash
mkdir -p apps/web
mkdir -p apps/mini
mkdir -p packages/services
mkdir -p packages/modules
mkdir -p packages/request
mkdir -p packages/shared
4️⃣ 初始化子包(以 modules 为例)
bash
cd packages/modules
pnpm init
修改 package.json:
json
{
"name": "@repo/modules",
"version": "1.0.0",
"private": true,
"main": "./index.ts",
"types": "./index.ts"
}
5️⃣ 在应用中引用
在 apps/web 中执行:
bash
pnpm add @repo/modules --workspace
然后即可直接使用:
ts
import { useUser } from "@repo/modules"
六、依赖管理(核心原则)
🔥 原则一:依赖就近声明
text
在哪使用,就在哪声明依赖
❌ 错误做法:
text
所有依赖都装在根目录
👉 会导致:
text
依赖污染 + 隐式依赖
🔥 原则二:单向依赖
text
apps → modules → services → request
👉 严禁:
text
modules → apps
services → modules
🔥 原则三:公共依赖再提升
例如:
bash
pnpm add -wD typescript eslint
七、TypeScript 与构建优化
1️⃣ 类型闭环(强烈推荐)
在 services 中定义 API 类型:
text
后端变更 → TS 报错 → 前端即时修复
👉 好处:
text
把"线上错误"变成"编译错误"
2️⃣ Turborepo(进阶优化)
安装:
bash
pnpm add turbo -wD
配置 turbo.json:
json
{
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"]
}
}
}
👉 带来的能力:
text
并行构建 + 缓存加速
八、最常见的 3 个坑
❗ 1. 依赖循环(Circular Dependency)
text
modules → shared
shared → modules ❌
👉 解决:
text
抽象更底层包,保持单向依赖
❗ 2. 编译入口问题
部分环境(如小程序)不支持直接引用 TS 源码。
👉 解决:
text
配置 exports / alias / 构建输出
❗ 3. 配置冗余
每个包都写 tsconfig 很麻烦。
👉 解决:
json
// tsconfig.base.json
子包继承:
json
{
"extends": "../../tsconfig.base.json"
}
九、这一步完成后,你得到了什么?
✅ 工程能力提升:
text
一次修改,多端生效
逻辑复用能力大幅提升
类型安全贯穿全链路
🔥 更重要的是:
text
你的代码开始"结构化"
十、总结一句话
text
Monorepo 不是工具,而是工程组织方式
🎯 结语
很多人觉得工程复杂,是因为工具太多。
但本质是:
text
代码没有边界
👉 Monorepo 的意义是:
text
让代码有"归属",让复杂度可控
🚀 下一篇预告
到这里,你已经完成:
text
架构设计(第2篇)
+ 工程落地(第3篇)
👉 下一篇,我们进入最核心的一步:
text
多端架构最难的 3 个问题(request / modules / design system)
🔥 这一篇,会是整个系列的"认知分水岭"。