1.概念
- Monolith 单体仓库
- Multi-Repo 多仓库
- Mono-Repo 单仓库
三种主流的代码仓库管理策略。它们代表了三种不同的哲学和工程实践,各有优劣,适用于不同的场景。

上图是git在不同策略的维护代码的效果,Mono-Repo最终还是回归单个仓库。
1. Monolith(单体仓库)
核心概念
将所有功能、模块和代码都放在一个巨大的、单一的代码库和应用程序中。所有代码共享同一个构建过程、依赖树和部署流水线。
特点
- 单一代码库:所有前端、后端、脚本都在一个项目里。
- 统一构建和部署:一个命令构建整个应用。
- 紧密耦合:模块间通常直接相互引用,没有明确的物理边界。
- 共享依赖 :所有模块使用同一份
package.json
和依赖版本。
项目结构示例
bash
monolith-app/
├── src/
│ ├── components/ # 前端组件
│ ├── pages/ # 页面
│ ├── utils/ # 工具函数
│ ├── api/ # API 路由(后端)
│ └── models/ # 数据模型
├── package.json # 所有依赖
└── vite.config.js # 统一构建配置
优点
- 开发简单:初期设置简单,无需考虑代码分割和依赖管理。
- 代码复用容易:所有代码在同一个项目,可以直接引用。
- 功能调用直接:没有网络开销,如同函数调用一样简单。
- 易于调试:可以在一个IDE中跟踪整个调用链路。
缺点
- 难以扩展:随着项目增长,构建时间变长,代码库变得臃肿。
- 技术栈锁定:所有部分必须使用相同的技术栈和依赖版本。
- 部署风险高:一个小修改需要重新部署整个应用。
- 团队协作困难:多人协作容易产生冲突,权限管理粗粒度。
适用场景
- 小型项目或初创公司MVP
- 团队规模小(2-5人)
- 项目生命周期早期
- 功能模块间耦合度极高的应用
2. Multi-Repo(多仓库)
核心概念
将系统拆分成多个独立的、松耦合的服务或包,每个都有自己独立的代码仓库、构建流程和部署流水线。
特点
- 独立仓库:每个微服务或包都是一个独立的git仓库。
- 独立部署:每个服务可以独立部署,互不影响。
- 明确边界:通过API(REST、GraphQL等)或消息队列进行通信。
- 技术异构:不同服务可以使用不同的技术栈。
项目结构示例
bash
# 每个都是独立的Git仓库
user-service/ # 用户服务
├── src/
├── package.json
└── Dockerfile
order-service/ # 订单服务
├── src/
├── package.json
└── Dockerfile
frontend-app/ # 前端应用
├── src/
├── package.json
└── vite.config.js
shared-lib/ # 共享工具库
├── src/
└── package.json
优点
- 独立开发和部署:团队可以独立工作,互不阻塞。
- 技术灵活性:不同服务可以选择最适合的技术栈。
- 故障隔离:一个服务崩溃不会影响整个系统。
- 易于扩展:可以针对特定服务进行水平扩展。
缺点
- 代码复用困难:共享代码需要发布到包管理器(如npm)。
- 版本管理复杂:需要协调不同服务间的API版本。
- 开发环境复杂:需要运行多个服务,调试困难。
- 分布式系统复杂度:需要处理网络延迟、服务发现等问题。
适用场景
- 中大型分布式系统
- 多个独立团队协作
- 需要不同技术栈的项目
- 高可用性和可扩展性要求的系统
3. Mono-Repo(单仓库)
核心概念
折中方案 :将多个独立项目放在同一个git仓库中,但保持它们逻辑上的独立性。每个项目有自己的package.json
和构建配置,但共享一些基础设施。
特点
- 统一仓库:所有项目在同一个git仓库中。
- 独立项目:每个项目有独立的配置和依赖。
- 共享工具链:统一的lint、test、build配置。
- 工作空间支持:使用pnpm/npm/yarn workspaces管理依赖。
项目结构示例
bash
mono-repo/
├── packages/
│ ├── web-app/ # 前端应用
│ │ ├── src/
│ │ ├── package.json
│ │ └── vite.config.js
│ ├── mobile-app/ # 移动端应用
│ │ ├── src/
│ │ ├── package.json
│ │ └── react-native.config.js
│ ├── api-service/ # API服务
│ │ ├── src/
│ │ ├── package.json
│ │ └── Dockerfile
│ └── shared-lib/ # 共享库
│ ├── src/
│ └── package.json
├── package.json # 根包,包含工作区配置
├── pnpm-workspace.yaml # pnpm工作区配置
└── turbo.json # Turborepo构建配置
优点
- 代码共享方便:可以直接链接本地包,无需发布到npm。
- 统一工具链:一致的代码质量、测试和构建流程。
- 原子提交:跨多个项目的修改可以在一个提交中完成。
- 简化依赖管理:工作区可以提升公共依赖。
- 更好的可见性:所有代码都在一个地方,易于发现和理解。
缺点
- 仓库体积大:所有历史记录都在一个仓库中。
- 权限管理复杂:细粒度的权限控制较困难。
- 构建工具要求:需要支持monorepo的工具(Turborepo、Nx等)。
- 心理负担:开发者需要理解整个代码库结构。
适用场景
- 多个相关的前端应用
- 组件库 + 使用示例
- 全栈应用(前端 + API)
- 中等规模团队,需要代码共享但不想管理多个仓库
2.实践 Monorepo:Vue3+组件
pnpm + workspace 是很好的实现Monorepo项目方案。
下面演示将通过pnpm + workspace 实现 Vue3+组件的Monorepo。
大致项目结构如下,包含一个自定义组件包和一个 Vue 应用项目。vue-app项目通过引用ui-components项目,实现组件的渲染
项目结构
js
my-workspace-project/
├── packages/
│ ├── ui-components/ # 自定义组件包
│ └── vue-app/ # Vue 应用项目
├── package.json
└── pnpm-workspace.yaml
运行后的效果

代码实现
1. 创建项目根目录和配置文件
bash
# 创建项目目录
mkdir my-workspace-project
cd my-workspace-project
# 初始化根目录的 package.json
pnpm init
# 创建 pnpm workspace 配置文件
echo "packages:
- 'packages/*'" > pnpm-workspace.yaml
2. 创建自定义组件包
bash
# 创建组件包目录
mkdir -p packages/ui-components
cd packages/ui-components
# 初始化组件包
pnpm init
编辑 packages/ui-components/package.json
:
这里生存的main 可以改成
"main": "./dist/index.js",
但这样每次要手动build一下项目,但比较麻烦,建议使用./src/index.js
调试,实时性强
json
{
"name": "@my-workspace/ui-components",
"version": "1.0.0",
"description": "Custom UI components",
"main": "./src/index.js",
"type": "module",
"files": [
"dist",
"src"
],
"scripts": {
"build": "vite build",
"dev": "vite build --watch",
"serve": "vite"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.5.0",
"vite": "^4.5.0",
"vue": "^3.3.0"
},
"peerDependencies": {
"vue": "^3.3.0"
}
}
创建组件文件 packages/ui-components/src/MyButton.vue
:
html
<template>
<button class="my-button" :class="[type, size]" @click="$emit('click')">
<slot></slot>
</button>
</template>
<script setup>
defineProps({
type: {
type: String,
default: 'primary',
validator: (value) => ['primary', 'secondary', 'danger'].includes(value)
},
size: {
type: String,
default: 'medium',
validator: (value) => ['small', 'medium', 'large'].includes(value)
}
})
defineEmits(['click'])
</script>
<style scoped>
.my-button {
border: none;
border-radius: 4px;
cursor: pointer;
font-family: inherit;
transition: all 0.2s;
}
.primary {
background-color: #409eff;
color: white;
}
.secondary {
background-color: #67c23a;
color: white;
}
.danger {
background-color: #f56c6c;
color: white;
}
.small {
padding: 8px 12px;
font-size: 12px;
}
.medium {
padding: 12px 16px;
font-size: 14px;
}
.large {
padding: 16px 20px;
font-size: 16px;
}
.my-button:hover {
opacity: 0.8;
}
</style>
创建入口文件 packages/ui-components/src/index.js
:
javascript
import MyButton from './MyButton.vue'
export { MyButton }
export default {
install(app) {
app.component('MyButton', MyButton)
}
}
创建 Vite 配置文件 packages/ui-components/vite.config.js
:
javascript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: './src/index.js',
name: 'MyUIComponents',
fileName: 'index' // 这样会生成 index.js 和 index.umd.js
},
rollupOptions: {
external: ['vue'],
output: {
globals: {
vue: 'Vue'
}
}
}
}
})
3. 创建 Vue 应用项目
bash
# 创建 Vue 应用目录
mkdir vue-app
cd vue-app
# 使用 Vite 创建 Vue 项目
pnpm create vite@latest . --template vue
编辑 packages/vue-app/package.json
,添加对组件包的依赖:
下面这个关联
"dependencies": { "@my-workspace/ui-components": "workspace:^" },
是通过 pnpm --filter @my-workspace/vue-app i @my-workspace/ui-components
生成
json
{
"name": "@my-workspace/vue-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@my-workspace/ui-components": "workspace:^",
"vue": "^3.5.21"
},
"devDependencies": {
"@vitejs/plugin-vue": "^4.6.2",
"vite": "^4.5.14"
}
}
修改 packages/vue-app/src/App.vue
来使用自定义组件:
html
<template>
<div id="app">
<h1>My Vue App with Custom Components</h1>
<MyButton type="primary" size="large" @click="handleClick">
Primary Button
</MyButton>
<MyButton type="secondary" size="medium">
Secondary Button
</MyButton>
<MyButton type="danger" size="small">
Danger Button
</MyButton>
</div>
</template>
<script setup>
import { MyButton } from '@my-workspace/ui-components'
const handleClick = () => {
console.log('Button clicked!')
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
text-align: center;
padding: 20px;
}
.my-button {
margin: 10px;
}
</style>
修改 packages/vue-app/src/main.js
:
javascript
import { createApp } from 'vue'
import App from './App.vue'
import MyUIComponents from '@my-workspace/ui-components'
const app = createApp(App)
app.use(MyUIComponents)
app.mount('#app')
4. 安装依赖并构建
bash
# 回到根目录
cd ../..
# 安装所有工作区的依赖
pnpm install
# 构建组件包
pnpm --filter @my-workspace/ui-components build
# 运行 Vue 应用
pnpm --filter @my-workspace/vue-app dev
5. 添加便捷脚本
在根目录的 package.json
中添加:
json
{
"scripts": {
"dev:components": "pnpm --filter @my-workspace/ui-components dev",
"build:components": "pnpm --filter @my-workspace/ui-components build",
"dev:app": "pnpm --filter @my-workspace/vue-app dev",
"build:app": "pnpm --filter @my-workspace/vue-app build",
"dev": "pnpm run dev:components & pnpm run dev:app"
}
}
6.使用
- 开发组件 :运行
pnpm dev:components
监听组件变化 - 开发应用 :运行
pnpm dev:app
启动 Vue 应用 - 同时开发 :运行
pnpm dev
同时启动组件和应用开发
3.常用pnpm workspace命令
1. 依赖管理命令
bash
# 在根目录安装所有工作区项目的依赖
pnpm install
# 等价于
pnpm i
为特定项目添加依赖
bash
# 为指定包添加生产依赖
pnpm --filter <package-name> add <dependency>
# 示例:为 web-app 添加 react
pnpm --filter web-app add react
# 添加开发依赖
pnpm --filter <package-name> add -D <dependency>
# 示例:为所有包添加 TypeScript
pnpm --filter "*" add -D typescript
--filter 简写 -F
sh
pnpm -F web-app add react
为根目录添加依赖
bash
# 根目录通常用于共享工具(eslint、prettier等)
pnpm add -wD eslint prettier
# -w 表示 workspace root(工作区根目录)
# -D 表示 devDependency
删除依赖
bash
# 删除特定包的依赖
pnpm --filter <package-name> remove <dependency>
# 示例:删除 web-app 的某个包
pnpm --filter web-app remove lodash
2. 项目过滤命令
基本过滤语法
bash
# 运行特定包的脚本
pnpm --filter <package-name> <command>
# 示例:运行 web-app 的 dev 脚本
pnpm --filter web-app dev
3. 依赖查看和分析命令
查看依赖树
sh
# 查看特定包的依赖树
pnpm --filter <package-name> list
# 查看整个工作区的依赖树
pnpm list -r
# 查看为什么安装了某个包
pnpm why <dependency> -r
检查依赖问题
sh
# 检查所有包的依赖问题
pnpm audit -r
# 修复依赖问题
pnpm audit fix -r
4.版本管理和发布命令
版本管理
bash
# 查看所有包的版本状态
pnpm versions check
# 交互式版本更新
pnpm versions update
# 批量更新版本
pnpm versions update --major
pnpm versions update --minor
pnpm versions update --patch
发布包
bash
复制
# 发布所有有变更的包
pnpm -r publish
# 干运行(测试发布)
pnpm -r publish --dry-run
代码质量检查
bash
# 在所有包中运行 lint
pnpm --filter "*" lint
# 运行测试
pnpm --filter "*" test
# 只运行有变更的包的测试
pnpm --filter "[origin/master]" test