Monorepo 单体式仓库

是什么

Monorepo 它代表”单一代码仓库”(Monolithic Repository)是一种项目代码管理方式,指单个仓库中管理多个项目,有助于简化代码共享、版本控制、构建和部署等方面的复杂性,并提供更好的可重用性和协作性。

为什么会出现

随着时间的推移,软件项目变得更加复杂,且对共享代码与复用逻辑的需求越来越强烈。这带来了对模块化的需求,旨在减少重复代码,实现更高效的协作与集成。

这种方法已经被很多大型公司广泛使用,如 Google、Facebook 和 Microsoft 等。

Monorepo 目录结构

.
├── .git
├── packages
│   ├── package-1          # 子包 1
│   │   ├── src
│   │   │   └── index.js
│   │   └── package.json
│   └── package-2          # 子包 2
│       ├── src
│       │   └── index.js
│       └── package.json
└── package.json

Monorepo 与 Multirepo 比较

  • 组织结构: Monorepo 将所有相关项目放在单个仓库中,Multirepo 则是为每个项目分配不同的仓库。
  • 项目独立性: Monorepo 可能需要更多的协作和协调,Multirepo 则可能更注重项目或团队的独立性。
  • 工具链复杂性: Monorepo 可能需要更先进的工具支持,但简化了共享和依赖管理,Multirepo 则在此方面可能更加繁琐。

Monorepo 管理工具

pnpm 管理 Monorepo

自带轻量的 Monorepo 的解决方案。

全局安装 pnpm

npm install -g pnpm

初始化项目

首先创建一个新的目录作为 Monorepo 的根目录,并在其中初始化 git 和 pnpm。

# 创建项目文件夹
mkdir my-monorepo && cd my-monorepo

# 根目录创建 package.json
pnpm init

# 创建两个子包
mkdir packages
mkdir packages/package-a && mkdir packages/package-b

# 初始化子包的 package.json
cd packages/package-a && pnpm init
cd ../package-b && pnpm init

# 最后再给每个子包创建一个 src/index.js

现在你已经有了两个子包:package-apackage-b

管理私有包:如果你不希望某个包被发布,确保 package.json 中设置 "private": true

指定工作区

在 Monorepo 根目录中创建 pnpm-workspace.yaml 文件,指定工作区的位置。

packages:
  - 'packages/*'

最终目录结构如下:

.
├── .git
├── packages
│   ├── package-a          # 子包 a
│   │   ├── src
│   │   │   └── index.js
│   │   └── package.json
│   └── package-b          # 子包 b
│       ├── src
│       │   └── index.js
│       └── package.json
├── package.json
└── pnpm-workspace.yaml

依赖管理

在 Monorepo 项目中,依赖分为两种:

  • 共有依赖: 在主包根目录下安装的依赖,会被所有子包继承。
  • 子包依赖: 在子包中安装的依赖,只会在当前子包中生效。

共有依赖

设置 TS、ESLint 等共享配置。

pnpm add -w -D typescript eslint
  • -w 表示安装到工作空间(即根目录)
  • -D 表示安装开发依赖

子包依赖

pnpm --filter package-a add lodash # 可以简写为 -F 
  • -F 表示安装到指定子包

包之间的依赖

还可以将工作区中的一个子包作为依赖安装到另一个指定的子包中,例如:安装 package-a 子包到 package-b 子包中:

pnpm -F package-b add package-a

执行完后,package-b 子包的 package.json 中会添加 package-a 子包的依赖,如下:

{
  "name": "package-b",
  "version": "1.0.0",
  "dependencies": {
    "package-a": "workspace:^"
  }
}

workspace:^ 表示安装的是工作空间中的 package-a 子包,依赖的子包版本相关值还有 workspace:*, workspace:~。资料参考: pnpm - 工作空间(Workspace){:target=“_blank”}。

在打包或者发布后会被转化为:

{
  "dependencies": {
    "package-a": "^1.0.0"
  }
}

总结

Monorepo 是软件开发实践中的一种趋势,其根据项目需求和开发团队规模逐渐演变发展。通过持续改进和优化,Monorepo 显著提升了大型项目的协作效率,减少了管理成本,并可以作为大规模开发环境下的有效解决方案。

参考

pnpm - 快速的,节省磁盘空间的包管理工具
monorepo.tools

> cd ..