一探 Monorepo 架构

101 阅读3分钟

monorepo

monolithic repository,单体仓库,把多个仓库放到了一个仓库来管理。

特别适合那些多个联系密切的包,React、Vue3,Jest,Next 等都采用这些方式

常规的 npm 包管理方式 multi repository

  • 多个包独立存在,单独管理。比如当一个通用包变更后,依赖它的包需要修改,需要横跨多个项目,非常不利于维护,为什么不把他们放到一个仓库。
  • 在多个包反复执行 npm publish 等操作

git submoudle

git submoudle 也可以实现多个仓库的管理,主项目通过 git submodule 方式引入

  • 主项目保存了子项目的依赖链接,使用 git submodule update 要小心,不能搞乱了依赖关系。平常基本都是 git pull, 如果忘记 git submodule update 可能会把旧的submodule提交上去。
  • git submodule 依然是多个仓库,仍会存在横跨多个仓库的来回修改的问题。

pnpm

快速的,节省磁盘空间的包管理工具。它具有 workspace 的能力。npm 从 7 开始也支持 workspace,当然 yarn 也支持。workspace 允许你从工作区链接这些 packages,多个 package 可以共享一套流程,比如eslint,git hook。

{

    "dependencies": {

        "svc": "workspace:^1.5.0"

    }

}

在发布之后将会自动替换为正式的版本

{

    "dependencies": {

        "svc": "^1.0.0"

    }

}

npm 、yarn 存在的问题

之前的 npm 结构是树形的,package.json 里的依赖包会被安装到 node_modules 一级目录,依赖包内部的子依赖会被安装到子包自己的 node_modules 中,这样一层一层,会占用大量的磁盘空间,其次就是这种层层的结构会造成引用路径过长。在 npm3 中 把依赖拍平了,依赖扁平化。

> 

    ├── node_modules

    ├── lodash

    |   └── index.jss

    |   └── package.json

    ├── moment

       ├─ index.js

       └─ package.json
  • 幽灵问题

因为包被拍平了,会出现在 package.json 没有声明的情况下也可以使用其他包,你能访问到并不属于你自己的依赖。

  • 分身问题

pac1 -> moment.js@1.0.0

pac2 -> moment.js@1.0.1

  • 相同的包也会安装两次
  • 依赖结构的不确定性,当项目中的依赖使用相同包时容易造成引用问题

├── node_modules

├── pac1

| └── index.js

| └── package.json

├── pac2

| └── node_module

| └── moment@1.1.0

└── index.js

└── package.json

├── moment@1.0.0

└── index.js

└── package.json

或者是这样呢

├── node_modules

├── pac1

| └── node_module

| └── moment@1.0.0

| └── index.js

| └── package.json

├── pac2

└── index.js

└── package.json

├── moment@1.1.0

└── index.js

└── package.json

取决于你package.json 中 pack1、pack2 的顺序,后来 npm 5 的时候增加了 lock 文件,来保证 node_module的确定性。npm,yarn 扁平化的安装机制造成包可以被访问问题,另外就是扁平化机制算法复杂,耗时长。

pnpm 登场

他不会重复对包进行安装,依赖被存储起来了,所有文件都被存储到硬盘的某个文职,下次你在安装的时候,会直接硬链接到这些文件。如果有一天你更新了某个依赖,新版本有文件改变了,pnpm会额外添加文件,只处理增量,不会再添加新版本的整个包。pnpm通过软硬链接的方式解决了幽灵依赖和npm包分身的问题。基于符号链接的 node_modules 结构

pnpm支持强大的过滤,允许将命令限制于包的特定子集。

pnpm run test --filter=@chehejia/idaas-sdk-app

changesets 版本发布管理

一切就绪,现在就差发布版本了。

常规情况下每个 package 是一个 repo,有自己的 commit 和 changelog,多包情况下每个 package 都需要有自己的版本号,有些工具可以通过 commit 自动生成 changelog,这种情况下不能用最外面的那个 commit 作为 changelog,需要针对每个 package 单独维护

workspace 中的包版本管理是一个复杂的任务,pnpm官方推荐 changesets 和 Rush,我们采用了 changesets。changesets 能灵活的控制每个包。

其他解决方案,比如 yarn workspace + lerna

利用 yarn workspace 能力,实现多个 packages 的共用,利用 lerna 进行版本管理。lerna 是一种针对多包管理工作流进行优化的工具,兼具版本管理的功能, lerna 曾宣布停止维护,后由 nrwl 公司接管继续维护。