cssmagic / cssmagic/Footprint

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

Open
#14 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

架构方案
Dominant language
No language data
Stars
37
Forks
3
PR merge metrics
No merged PRs in 30d

Description

时间 2022
所在公司 RJ(2345 子公司)
角色 技术总监 / 架构师

项目背景

  • 一个项目由若干个子项目组成。比如一个 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 = 订阅

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

This issue is an experience-sharing article about a JavaScript Monorepo workflow, not a concrete change request. Read the article, the linked issue #13, and the referenced multi-mpa project first; no acceptance criteria or specific file to update is provided, so a newcomer cannot determine what completion would mean.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
documentation
Issue type
Documentation
Difficulty
1/5
Estimated time
Under an hour
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.