Monorepo 快速上手

什么是 Monorepo

英文全称其实是 Monolithic Repository, 即单一仓库方案. 将多个项目整合在一个仓库中, 让这些仓库可以公用一些通用的东西.

优势

  1. 便于代码和依赖在多个项目之间的 共享.
  2. 更方便简单的项目版本控制. 可以分别控制大项目和小项目的版本.
  3. 提高多项目的构建与部署的便捷性.
  4. 提高代码的复用性和团队协作的便利性.

应用

  1. 项目的子项目架构, 一个大项目的几个小部分进行分开管理, 提升效率
  2. 对于工具分类分装的架构
  3. 对于复杂且丰富的应用需求的功能切片

仓库项目管理的发展历程

  1. 单体仓库单应用 - Monolith
    1. 即一个仓库管理一个单应用.
    2. 随着项目的功能迭代, 单体应用的规模会逐渐变大, 难以维护.
  2. 多仓库多应用 - Multirepo
    1. 多个应用分配给多个对应的仓库进行管理
    2. 缺点: 无法进行项目依赖与代码的共享.
  3. 单仓库多应用 - Monorepo
    1. 一个仓库管理多个应用
    2. 优点: 业务代码分离解耦, 应用间共享依赖与代码, 项目配置和构建部署, 项目扩展更方便.

创建 Monorepo 仓库

首先, 创建一个文件夹, 这个文件夹就是我们的 monorepo 仓库了.

对于一个 monorepo, 由以下部分组成:

  1. 应用 apps. 也就是一个大项目里面的子应用.
  2. 本地宝 packages. 本地的各种包, 可以相互之间调用.
  3. 依赖的共有和私有.
  4. 配置的共有和私有.

首先, 创建一个仓库.

1
pnpm init

随后创建对应的文件夹, 并且进行同样的操作, 可以得到下面这样的目录:

为了让 pnpm 知道这些是我们的包, 我们需要在根目录创建一个文件: pnpm-workspace.yaml, 并且在里面配置各种包:

1
2
3
packages:
- "apps/*"
- "packages/**"

这样就创建好子包了. 我们可以把通用的依赖直接安装在根目录中, 比如添加一个 vite.

1
pnpm add -w -D vite

现在, 这个包就是安装在全局了. 假如我们的 utils 里面有工具函数:

无论如何, 只要是一个包, 我们就需要一个入口文件. 在 utils 目录下面的 package.json 中, 可以配置一下名称和入口:

1
2
3
4
5
6
7
8
9
10
{
"name": "@lc/utils",
"version": "1.0.0",
"description": "",
"main": "./src/index.js",
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.14.0"
}

这里的名称十分重要, 绝对不能配置错了.

随后, 假如我有一个应用, 比方说我在 app 目录中创建一个 vite 的项目, 直接使用上面定义好的名称进行安装即可. (后面需要有 --workspace 的后缀)

1
pnpm add @lc/utils --workspace

查看项目依赖, 出现了!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"name": "@lc/vite-project",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"@lc/utils": "workspace:*",
"vue": "^3.5.18"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.1",
"vite": "^7.1.2"
}
}

现在就可以直接引入东西了.

1
2
import {addTwoNum} from "@lc/utils"
console.log(addTwoNum(10, 20))

随后, 我们就算在根目录中, 也可以直接启动这个项目, 只要知道名称即可:

1
pnpm run --F @lc/vite-project dev

这样就实现了方便管理多个项目的功能, 以及多个项目公用同一个软件包.