mono repo 和 workspaces
mono repo 理论基础
什么是 mono repo 和 multi repo?
一个monorepo是一个版本控制代码库中保存了许多项目。尽管这些项目可能相关,但它们在逻辑上通常是独立的,并由不同的团队运行。
Monorepos 有时被称为单体存储库.一些公司将所有代码托管在一个存储库中,供所有人共享。
mono repo的特点
共同点
- 独立性:每一个项目都是独立的,也就是说都是可以单独的拿出的。mono-repo 是相对独立,multi-repo 是绝对对立的
mono repo 的优缺点
- 可见性强
- 更加简单的依赖管理
- 共享性
- 统一性
- 学习曲线
- 糟糕的性能
- 大量数据
- 版本控制
- ...
谁支持了 monorepo 的 workspace
为了在一个文件下管理多个平行的代码库(monorepo),于是抱管理工具相继诞生了 workspaces 功能。
- yarn 支持 workspaces 功能
- npm7 开始支持 workspaces 功能
- pnpm 支持 workspaces 功能
- Lerna 命名行工具支持管理 workspaces
yarn workspace 使用示例
1. 使用 yarn 开始一个 monorepo
mkdir <project_name>
cd <project_name>
yarn init -y
注意如下两个字段必须存在:
- 字段 version
- 字段 name
2. 改造 package.json 文件,增加 workspaces/private 字段
{
"private": true, // 必须是一个私有的包
"name": "<your_project_name>",
"version": "1.0.0",
"workspaces": [
"packages/*",// 将所有 packages 下所有的文件作为 mono repo
],
"main": "index.js",
"license": "MIT",
"dependencies": {
"lodash": "^4.17.21"
}
}
3. 手动增加 project1/project2 项目文件夹
├── package.json # 顶层 package.json
├── packages
│ ├── project1 # mono repo 1
│ └── project2 # mono repo 2
└── yarn.lock
建好文件夹后,初始化两个项目, 因为只有初始化两个项目之后才能,使用 workspace 的相关命令,否则 yarn 任务 project1/project2 不是一个项目。
cd packages/project1
yarn init -y
cd ../packages/project1
yarn init -y
当然如果,你使用的 umijs/vue 等框架,可以使用其脚手架工具来初始化一个项目。
4. 在 project1/project2 项目中写代码
操作 mono repo 的命令:
yarn workspace <workspace_name> <command>
例如,我们要在project1 中使用 React 开发项目,在项目根目录执行:
yarn workspace project1 add react react-dom
project2 中我们添加 vue
yarn workspace project2 add vue vue-router vuex
安装之后,查看根 package.json 文件没变化,但是 project1/project2 项目的 package.json 发生了变化。发现所用 yarn workspace 命令安装包 project1/project2 中没有生成 node_modules, 两个项目的依赖包都放在了根 node_modules。
5. 使用常用 @umijs 生成 project3
cd packages/
mkdir project4 && cd project3
yarn create @umijs/umi-app # 不会安装 node_modules
# 手动安装 node_modules
cd porject3
yarn workspace project3 install
# 启动 umi 服务
yarn workspace project3 start #http://localhost:8000
6. 使用常用 @vue/cli 生成 project4
cd packages/
vue create project4 # vue 会帮我们安装
cd project4
rm -rf node_modules
cd ../../
yarn workspace project4 install
# @vue/cli 自动的安装,产生一个 node_modules
cd project4 && rm -rf node_modules
cd ../../ && yarn workpsace project4 install
## 启动 vue 服务
yarn workspace project4 serve # http://localhost:8080/
pnpm workspace 示例
注意 node.js 版本支持(推荐 12 版本)
1.初始化一个 pnpm monorepo 项目
cd your_project
pnpm init -y
2. 创建了一个 pnpm-workspace.yaml 的 monorepo 的配置文件。
touch pnpm-workspace.yaml
mkdir packages && cd packages && mkdir project1 project2
# 添加 .npmrc 文件在子项目根目录下
cd packages/project2 && touch .npmrc
cd ../project1 && touch .npmrc
配置 pnpm-workspace.yaml 文件
packages:
- 'packages/**'
同时注意,在 workspace 所在项目下,添加 .npmrc 配置文件。
3. 手动在 project1/project2 中添加依赖项目
pnpm 使用 add/install 命令添加以来包(区别查看官网资料), 在 workpakce 中添加需要 --filter <workspace-name> 标识。
# 在顶层添加 lodash 进行测试
cd project
pnpm add lodash
## 在 project1 中添加 react 项目
pnpm add react react-dom --filter project1
## 在 project2 中添加 vue 项目
pnpm add vue vue-router vuex --filter project2
安装好之后,在对应的 package.json 下面写入了依赖,在目录下 package.json 的内
4. 使用 @umijs 生成 React 项目
cd packages && mkdir project3
cd project3 && pnpx @umijs/create-umi-app
# 在 umijs package.json 文件中添加 name 字段
"name": "project3"
cd ../ && pnpm install --filter project3
## 启动 umijs 服务
pnpm run start --filter project3 # http://localhost:8000
5.使用 @vue/cli 生成 React 项目
cd packages && vue create project4
# vue 会安装node_modules,需要删除,在根目录下重新安装
cd project4 && rm -rf node_modules
# 重新安装
pnpm install --filter project4
# 启动服务
pnpm run serve --filter project4 # http://localhost:8080/
npm7 workspace 示例
npm 的 workspace 与 yarn/pnpm 的 workspace 时有区别的。它的主要功能:
npm workspace 弥补了从本地文件系统处理链接包的更加简化的工作流程。自动化链接过程作为一部分,npm install 避免手动使用 npm link 以添加对应符号链接到当前 node_modules 文件夹的包的引用 。
也就是说 npm workspace 中定义的包是用来分享的用。目的是简化 npm link 的链接
npm workspace 使用示例
# 初始化一个根项目
cd your_path && mkdir your_project && cd your_project
npm init -y
# 添加 lodash
npm i lodash
1. 开起 workspace 功能
注意:npm 从 7 版本开始支持 workspace 功能。
"name": "your_name",
"workspace": [
"./packages/*"
]
2. 创建 React 项目
cd packages
npx create-react-app app1
3. 在跟目录中安装(或者 link app1 模块)
cd ../
npm install app1 --workspace=app1
这样我们就在根目录中安装了,workspace 中的 app1 包,于是就可向使用其他包一样使用 app1 了。
Lerna mono-repo 管理工具
lerna 可以理解为一个 mono-repo 的命令行工具。
1. 全局安装 lerna
npm install --global lerna
yarn global add lerna
2. 使用 lerna 必须初始化 git:
cd your_project_path && mkdir your_project && cd your_project
git init
3. 使用 lerna 命令初始化一个项目
lerna init
lerna 初始化之后得到的目录结构
.
├── lerna.json
├── package.json
└── packages
4. 使用 lerna 命令行创建两个字命令
lerna create project1
lerna create project2
再次查看目录情况:
.
├── lerna.json
├── package.json
└── packages
├── project1
│ ├── README.md
│ ├── __tests__
│ │ └── project1.test.js
│ ├── lib
│ │ └── project1.js
│ └── package.json
└── project2
├── README.md
├── __tests__
│ └── project2.test.js
├── lib
│ └── project2.js
└── package.json
5. 使用 lerna 运行子项目脚本
其实与 npm 基本相同,只是将 npm 改为 lerna 命令
lerna run test
注意:必须切换到每一个包里面
6. lerna 命令的简单解析
- lerna bootstrap 将 npm 包安装到根目录下的 node_moduels 中。
- lerna exec 在包中执行命令
lerna exec -- < command > [..args]
7. lerna 的配置文件
{
"useWorkspaces": true, // 使用 workspaces 配置。此项为 true 的话,将使用 package.json 的 "workspaces",下面的 "packages" 字段将不生效
"version": "0.1.0", // 所有包版本号,独立模式-"independent"
"npmClient": "yarn", // npm client,可设置为 yarn 等
"packages": [ // 包所在目录,可指定多个
"packages/*"
],
"command": { // lerna 命令相关配置
"publish": { // 发布相关
"ignoreChanges": [ // 指定文件或目录的变更,不触发 publish
".gitignore",
"*.log",
"*.md"
]
},
"bootstrap": { // bootstrap 相关
"ignore": "npm-*", // 不受 bootstrap 影响的包
"npmClientArgs": [ // bootstr 执行参数
"--no-package-lock"
]
}
}
}
8. 与 yarn 的 workspace 配合
lerna 初始化的没有,有 workspace 进行标记,标记之后,其他的就和前面介绍的一样了。
mono-repo 的版本管理
参考
- workspaces docs.npmjs.com/cli/v7/usin…
- Simplify your monorepo with npm 7 workspaces dev.to/limal/simpl…
- What is monorepo? (and should you use it?) semaphoreci.com/blog/what-i…
- lerna lerna.js.org/