为什么要选择 Monorepo ❓
在软件开发中,代码仓库的管理对项目的开发🧐和协作👥有着很重要的作用。常见的管理方式有Monorepo(单体仓库)和Multirepo(多体仓库)两种。
Multirepo的痛点😖
Multirepo是使用单独仓库对单独项目进行管理,项目中的文件被放在不同的仓库中,这正是我们大多数开发所使用的代码管理方式;
痛点:
依赖问题😖
如果每个项目代码都安装依赖,都会产生
node_modules这样的文件,项目体积变大;如果更新依赖需要每个项目都进行更新,其中一个出现错误,其他项目可能也会出现相同的错误❌,需要同时更改多个文件;
重复配置
package.json、Eslint、Prettier、Typescript等多种工具或语言的配置需要在每个项目都进行配置,特别麻烦❗️跨仓库重构困难
当遇到重构某个接口任务时需要同时修改多个项目的代码,一个简单的重构任务变成了跨仓库的大工程🏗️
Monorepo介绍
而Monorepo是使用一个仓库对多个项目进行管理的项目管理策略,嫩个很好的解决Muitirepo的这些痛点❗️
共享依赖,统一版本
Monorepo项目的依赖统一在根目录下,所以相关的项目使用硬链接链接到根目录下的node_modules;当依赖更新时,所有项目的依赖都会更新,不需要单独更新;
统一配置
我们都知道代码环境的配置是很麻烦的,使用Multirepo就需要重复配置多次,而Monorepo只需配置一次就可多个项目共享。一次重构,原子提交
当修改了一个共享工具函数之后,可能需要更新多个地方,但是在Monorepo就可以一次更新(原子提交),就可以同时修改所有依赖它的项目。
比如说一个项目同时有UI组件库,工具库,服务器端代码,客户端代码等多项目代码,那么这个项目的结构就是这样的
my-project/ |
创建Monorepo项目
首先创建项目:
mkdir my-monorepo |
🫀核心文件 pnpm-workspace.yaml
接着创建文件pnpm-worspace.yaml,这个是Monorepo的🫀核心文件,它告诉pnpm哪些是要管理的子项目。配置:
packages: |
得到:
my-monorepo/ |
好,现在来创建子项目:
mkdir packages/utils |
进行packages.json的配置:name我们统一配置为@[项目名]/[子项目名],这样更规范一点
{ |
现在可以再创建一个子项目:
mkdir packages/web-app |
同样地,进行packages.json的配置:name我们统一配置为@[项目名]/[子项目名],这样更规范一点
{ |
像这样,我们直接在子项目中通过dependencies引入,就可以使用了。
增加便捷命令:
在根目录下的packages.json
{ |
这样在根目下就能直接启动某个子项目了。
我们接着来认识一下Turborepo🔧
Turborepo
Turborepo是Vercel出品的高性能Monorepo构建工具,可以用来管理多项目依赖,并行任务,完美适配React/Vue/Next.js框架
对比直接使用pnpm,Turborepo的优点有:
- 多任务并行 – 可同时启动多个子项目
- 构建速度快 –
Turborepo有缓存机制,能够更快的启动和打包 - …
所以更推荐选择使用pnpm+Turborepo来构建和管理我们的Monorepo项目。
引入Turborepo
如果跟我一样先创建了项目,又想使用Turborepo,那么就可以跟着我这样做:
# monorepo 根目录 |
接着在根目录package.json中配置:
{ |
但也可以只配置子项目的package.json,当在根目录执行turbo [xxx],turbo会自动在所有子项目中package.json中寻找名为...的命令,比如某子包的package.json:
{ |
就可以在根目录下执行
在子项目引入工具包
在子项目package.json中添加依赖:
{ |
之后执行pnpm install,就会发现依赖已经被链接到node_modules中了。
❗️注意
Monorepo结构下所有子项目都必须安装eslint,而且最好在8.x版本,不然可能会有unmet peer警告pnpm add eslint@latest -w --save-dev # 为所有子项目安装eslint到最新版本 -w / --workspace表示“对工作区内所有子包执行安装”
npx eslint --init # 选择 React/TypeScript 等配置,生成 .eslintrc.js
pnpm add eslint@8.57.0 --save-dev


