Skip to content

业务项目基于 Monorepo 的前端研发工作流 #14

Open
@cssmagic

Description

@cssmagic

项目背景

  • 一个项目由若干个子项目组成。比如一个 Hybrid 应用中的多个 H5 模块。
  • 各个子项目的形态类似,比如都是由 Vue CLI 创建的 Vue2 应用,它们有着高度相似的运行机制、目录结构、依赖和配置。
  • 这些子项目有一些公共依赖,比如公共组件、公共逻辑、工具库等。

要解决什么问题?

面对这样的项目,传统做法通常是这样的:

  • 每个子项目作为独立仓库来管理。
  • 公共依赖也是划分为多个仓库,比如公共组件一个仓库、公共逻辑一个仓库、工具库一个仓库。这些公共依赖作为 “内部包” 发布到公司的私有 NPM 仓库,被各个子项目引用。

这种做法可以运转,但存在以下问题:

  • 如果子项目的数量很多,则开发者需要面对大量仓库,不易维护。
  • CI 系统中的构建任务通常都与仓库绑定,那大量子项目就需要创建大量部署任务,不易使用和维护。
  • 每个子项目都要安装几乎一致的依赖,费时费空间。
  • 每个子项目的配置大同小异,却分散在不同仓库里,如果要改配置,需要在一批仓库中同步修改,维护成本高。
  • 在开发阶段,公共依赖(内部包)可以通过 npm link 等方式来实现与子项目的同步开发调试;但在部署阶段,需要先把内部包发布新版,然后子项目才能构建部署。整个流程并不顺畅。

怎么解决?

面对这类包含了多个子项目的大项目,我不由想到前端开源领域广泛应用的 Monorepo 模式。经过一番摸索,成功地把一个包含了几十个子项目的大型前端项目改造成了 Monorepo,日常的维护和部署流程都顺畅跑通,团队小伙伴直呼 “真香”!

Monorepo 目录结构示意:

/                  # 根项目
├── node_modules/
├── packages/
│   ├── _common/       # 公共业务逻辑
│   ├── _util/         # 公共工具库
│   ├── project-a/     # 业务项目
│   ├── project-b/     # 业务项目
│   └── (...)          # 更多业务项目
├── README.md
├── lerna.json
├── package.json
└── yarn.lock

Monorepo 轻松化解了上述 “传统做法” 的诸多问题:

  • “大量仓库不易维护”。这个问题显然解决了:所有相关联的业务项目(子项目)和公共依赖(内部包)都纳入单个大仓库,统一管理源码。

  • “CI 系统需要创建大量部署任务”。CI 系统只需要准备一个任务,开发者在启动任务时选择参与此次任务的子项目就可以了。Monorepo 工具链对此提供完善的支撑,具体效果参见本文末尾的截图。

  • “每个子项目重复安装依赖”。由 Yarn Workspace 完美解决了,所有子项目的相同依赖都被提升到了项目根级,省时省空间。

  • “每个子项目配置大同小异,却分散在不同仓库里”。现在所有子项目都在同一个仓库里,有机会把所有相同的配置提取出来。目前已经实现了 Git ignore、EditorConfig、ESLint、Browserslist、Vue CLI、NPM script 等常用配置的共性提取,同时允许子项目的个性化配置。

    这里顺便提一下,在解决了配置代码的 “DRY” 问题之后,我们还想继续提炼业务代码:为了消解子项目之间的代码结构差异,我们还把 Vue 项目的扩展需求(比如是否加载 UI 组件库、是否加载 Vuex 等)都做成了 “约定式” “可插拔” 设计。新建一个子项目的过程,就是拿项目模板复制一份出来,删删减减就可以得到业务所需的骨架代码了。

  • 公共依赖包与子项目的 “同步开发” 和 “部署次序” 问题。这个问题也舒适地解决了:一方面,公共依赖包与子项目在同一个大仓库里,可以同步开发,同步部署;另一方面,Yarn Workspace 会把依赖包通过软链接的形式 “安装” 到项目根级,子项目可以直接引用公共依赖包,开发阶段甚至连 npm link 都省掉了。

本文篇幅有限,更多细节补充如下:

附:已知问题

  • 多人在同一个大仓库中协作,并行的分支会非常多,对团队协作要求高。(如果团队的 Git 规范设计合理,且团队成员都能正常执行,这个问题可控。)

  • 单一仓库的体积较大,文件数量较多,IDE 在打开此仓库时性能消耗大。(在 IDE 中排除不需要关注的子项目目录,可以有效减轻影响。)

相关材料节选

  • Jenkins 上的构建任务(可以按需勾选本次部署的子项目):

    jenkins-job


相关链接


© 经验分享 · 日拱一卒   |   Star = 收藏   |   Watch = 订阅

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions