平行代码仓库(mono repo)

4,052 阅读6分钟

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 的版本管理

参考

  1. workspaces docs.npmjs.com/cli/v7/usin…
  2. Simplify your monorepo with npm 7 workspaces dev.to/limal/simpl…
  3. What is monorepo? (and should you use it?) semaphoreci.com/blog/what-i…
  4. lerna lerna.js.org/