什么是 monorepo

引用一下网上的介绍:

首先,我们解释一下什么是 monorepo 和 multirepo。这两者都是管理组织代码的方式,顾名思义 monorepo 就是把所有的相关项目都放在一个仓库中(比如 React, Babel…),multirepo 则是按模块分为多个仓库。

这两者的核心区别可以归结为你相信怎样的哲学能让团队在一起工作的效率最高(多元化 vs 集中管理)。

从 multirepo 的角度来看,这样让每个子团队拥有自己的 repo,可以用他们自己擅长的工具、workflow 等等。多元化能促使各个团队尽可能的提升自己的效率。

但代价也在于会增加很多沟通成本,如果你在你们项目用到的库中发现了一个 bug,就必须到目标库里修复它、打包、发版本,然后再回到你的库继续工作。在不同的仓库间,你不仅需要处理不同的代码、工具,甚至是不同的工作流程。

而从 monorepo 来看,让不同的团队走自己的路,并不见得能提高生产力。虽然有些团队可能会找到自己最佳的工作方式,但他们的收益也会被其他团队不那么好的工作方式所抵消。相反,严格统一的管理更能提升效率,团队中的任何人都可以(并且应该也被鼓励)修改任何东西(因为修改造成的结果马上就能展现出来,)。虽然把所有的鸡蛋都放进了一个篮子里,但我们也可以更小心的照顾这个篮子。

Vant 仓库现状

Vant 在迭代过程中衍生出了很多的子仓库,这些子仓库采用 multirepo 的方式进行分散管理,并分布在有赞 Github 账号、个人 Github 账号、内网等地方。仓库列表如下:

  • vant
  • vant-demo
  • vant-weapp
  • vant-cli
  • vant-doc
  • vant-icons
  • vant-waterfall
  • vant-eslint-config
  • vant-touch-emulator
  • vant-markdown-vetur
  • vant-markdown-loader
  • vant-issue-generater
  • vue-cli-template-vant

随着子仓库数量的不断增加,以 multirepo 管理的方式碰到了一些问题,有如下几点:

  • 需要重复配置 tsconfig、eslintrc、babelrc 等文件,并且配置没有同步更新。部分仓库升级到了 babel 7, 部分还停留在 babel 6, 有些仓库连 eslint 配置都没有
  • 仓库散落在各个地方,子仓库的存在感很低,全部放到有赞 Github 账号下又显得太臃肿
  • 子仓库会收到零星的 issue 和 PR,但常常会被忽略
  • 有些 issue 会被提到错误的仓库
  • 开发时需要在多仓库之间切换

monorepo 可以有效解决上述的问题,但也会引入一些其他问题,如:

  • 仓库会变得庞大,commit 记录中包含了各个子包的提交历史
  • 升级依赖需要更多的工作
  • 新人的上手成本上升(但长期而言有利于维护)

为了避免造出一个巨无霸仓库,需要适当的对仓库进行合并,最终保留以下四个仓库:

  • youzan/vant
  • youzan/vant-demo
  • youzan/vant-weapp
  • youzan/vant-issue-generater

其中,vant 仓库作为 monorepo 存在,在 vant/packages 目录下管理所有的子包,子包通过 npm 输出给 vant-weapp 和其他内网项目。vant-demo、vant-weapp 和 vant-issue-generater 作为独立的普通仓库继续维护(这三个仓库需要部署独立的 gh-pages 网站)。其他子仓库全部标识为 Archived 状态,并链接至 vant/packages 路径。

同时,对 vant 主仓库和子包做了如下改造:

  • 主仓库添加了 commit-msg 规范和自动化校验
  • 子包的 npm scope 全部收敛至 @vant 下,比如 vant-doc 重新命名为 @vant/doc
  • 子包的 babel、eslint、tsconfig 等配置全部删除,或从根目录继承
  • 子包的重复依赖全部删除
  • 子包的编译配置和主仓库统一
  • 子包的 README 采用统一格式
  • 后续计划引入 lerna 进行管理

改造后的目录长这样(比之前清爽了不少),后续添加子包也不需要新建仓库了,直接提交目录即可
image.png

总的来说,改造完之后,子包会得到更好的维护,Vant 的架构也向大型项目迈进了一步。

2019 年 08 月 23 日