Monorepo架构

Monorepo架构策略

更高效的代码共享和管理

为什么使用

项目不断膨胀 -> 构建速度↓↓↓ -> 包体积↑↑↑ -> 性能越来越慢

比如说想开发一个组件库,我在开发时需要启动 单组件测试、页面预览 两个服务,也就是需要在两个或多个项目中复用内部组件和逻辑,这时候我们就需要Monorepo架构来优化项目。

简要概述

项目结构 (以我的组件库项目为例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
root
├── node_modules/
├── ......

├── packages
│ ├── components(组件实现)
│ ├── node_modules/
│ ├── ......
│ └── package.json
│ ├── docs(项目一:组件库页面)
│ ├── node_modules/
│ ├── ......
│ └── package.json
│ └── play(项目二:组件测试页面)
│ ├── node_modules/
│ ├── ......
│ └── package.json

├── ......
├── pnpm-workspace.yaml
└── package.json //依赖会

这样我们可以同时启动多个项目,并能合并所有子项目共用的包到根目录下,在减少整体项目包体积的前提下,packages下的各项目能做到相互独立互不影响。

如何初始化

配置 pnpm-workspace.yaml

PNPM 工作区的配置文件,用于管理多个包之间的依赖关系

基础代码

1
2
packages:
  - "packages/*"

所有位于 packages 目录下的子目录都被视为独立的包,并且是工作区的一部分

扩展

例如

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

同样的,libs 目录下的子目录也被视为独立的包,被视为工作区的一部分

如果你有一个包在 packages 文件夹中依赖了另一个位于 libs 文件夹中的包,PNPM 不会从远程仓库下载该依赖,而是创建一个符号链接指向该本地包的位置。

当你在一个工作区内的任意一个包中安装依赖时,PNPM 会将这些依赖安装在工作区根目录下的 node_modules 文件夹内。工作区内所有的包可以共享相同的依赖版本,减少了重复安装和存储空间的浪费

配置 package.json

位于根目录,我的组件库项目叫tyche

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
"name": "tyche",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"dev": "pnpm --filter @tyche/play dev",
"test": "pnpm --filter @tyche/components test"
},
"packageManager": "pnpm@10.4.1",
"devDependencies": {//开发环境
......
},
"dependencies": {//生产环境
"@tyche/components": "workspace:*",
"@tyche/hooks": "workspace:*",
"@tyche/theme": "workspace:*",
"@tyche/utils": "workspace:*",
"tyche": "workspace:*",
......
}
}

/// 补充:peerDependencies ///

1
2
3
4
5
6
7
8
9
├── helloWorld
│ └── node_modules
│ ├── packageA
│ ├── plugin1
│ │ └── nodule_modules
│ │ └── packageA
│ └── plugin2
│ │ └── nodule_modules
│ │ └── packageA

packageA 核心依赖库被重复下载

此时我们应该在

plugin1 /package.json & plugin2 /package.json

1
2
3
4
5
{
"peerDependencies": {
"packageA": "1.0.1"
}
}

/package.json

1
2
3
4
5
{
"dependencies": {
"packageA": "1.0.1"
}
}

结构就变成了

1
2
3
4
5
├── helloWorld
│ └── node_modules
│ ├── packageA
│ ├── plugin1
│ └── plugin2

通常用于库或插件,这些库或插件不直接包含在最终应用中,但需要与其他包协同工作

当你安装一个包时,如果该包有peerDependencies,npm或yarn会检查这些依赖是否已经安装在项目中,如果没有,它会给出警告,但不会默认安装它们