基于Git子模块的微前端项目管理和公用组件库方案
基于Git子模块的微前端项目管理方案1. 媒体项目从单一项目到多项目的转变及问题随着前端媒体业务的急剧扩大,传统的单体应用已经变得难以维护,由此,这几年我们一直在探索对大型复杂项目的拆分工作。一开始拆分出去的是策划中心,老的媒体项目主体是AngularJS编写的,而拆分出去的策划中心模块则采用 VueJS 编写,限于框架之间的巨大差异,难以优雅地集成在一起,就使用了 Iframe 嵌入式方案。后来
基于Git子模块的微前端项目管理方案
1. 媒体项目从单一项目到多项目的转变及问题
随着前端媒体业务的急剧扩大,传统的单体应用已经变得难以维护,由此,这几年我们一直在探索对大型复杂项目的拆分工作。
一开始拆分出去的是策划中心,老的媒体项目主体是AngularJS编写的,而拆分出去的策划中心模块则采用 VueJS 编写,限于框架之间的巨大差异,难以优雅地集成在一起,就使用了 Iframe 嵌入式方案。
后来,随着配置管理模块、网络资源模逐渐使用 VueJS 重写为独立项目的工作完成,对采编模块的重写工作终于开始,未来版权资源和运营中心模块都会各自独立为一个项目。
各项目的集成工作提上了日程,经过一段时间的试验和摸索,最终使用了micro-app
库提供的微前端作为解决方案,基本实现了以采编中心为基座应用,其它模块作为子应用无缝集成的应用架构。
此时,就产生了新的问题:
- 随着所有模块都转而使用 VueJS 开发,而各个模块都属于典型的中后台项目,所以就会在各个模块产生很多相同UI、相同功能的组件,各个模块都要维护相同的组件,就会造成代码冗余以及重复的组件维护和功能升级工作。
- 每一个子应用都有自己独立的仓库,比较分散,不便于统一管理
- 每个子应用的依赖项都是单独管理,子应用间会产生大量重复依赖,浪费存储空间。
经过对目前可用的各种项目管理方案进行详细研究和对比,结合我们自己的项目特点,最终决定使用基于Git 子模块的项目管理方案。
2. 常见的项目管理方案研究
2.1 NPM
当提到跨项目组件复用时,我们第一时间想到的就是NPM仓库,即将公共组件都传到NPM里,各个项目直接通过NPM安装使用就可以。
NPM 方案的优点是:
- 非常成熟的依赖管理机制
- 具有成熟的规范
- 易于使用
它的问题在于:
- 我们的很多组件是跟业务相关的,不便于上传到NPM公共仓库中,所以就必须建立自己公司的私有NPM仓库,这会带来一些运维成本
- 当公共组件功能进行修复、升级时,项目要与其进行联调测试,而NPM包的发布流程又很繁琐,所以会拖慢调试速度,浪费大量时间在公共组件的发布流程上。
- 该方案只能解决跨项目组件复用问题,解决不了子应用项目集中管理和子项目重复依赖的问题
2.2 monorepo
monorepo
是单体式仓库,它的方案思路是在一个项目仓库中管理多个模块或包。即一个模块一个目录,全部装进一个仓库,统一拉取,统一发布。
这种方案的优点是:
- 一个地方管理主应用和所有子应用
- 所有子应用的Git活动日志和问题跟踪都在同一个仓库中管理
- 由于在同一仓库中,子应用间共享代码将变得简单
- 所有子应用和主应用保持一致的规范并一同进化,即统一的工作流
- 可以实现依赖共享
它的缺点是:
- 虽然各个模块子应用在代码上还是独立的,但是由于所有子应用代码都放在同一个仓库内,那仓库就会变得无比巨大
- 对于那些和主应用关联较弱,甚至可以独立部署的子应用,这种方式会增加其复杂度
- 实际应用中,业务仓库很难保持清晰的模块边界与依赖关系。
- 会产生依赖爆炸问题,并且依赖爆炸的解决与统一的工作流不可兼得。
2.3 Git 子模块
Git 子模块时 Git 原生提供的功能,它的依赖管理机制类似于NPM,但又有明显区别:
- NPM包管理的是子模块构建后的产物
- Git 子模块管理的依赖是子模块的源码
Git 子模块在目录结构上则与 monorepo 很相似,它其实相当于monorepo的各个模块抽离了独立的仓库,它与 monorepo的不同在于:
monorepo
在单个仓库里存放所有子模块源码- git子模块只在主仓库里存放所有子模块的索引
它的缺点是:
- 子模块与主项目是两个不同仓库,意味着需要为每个仓库创建一套脚手架(包含代码规范、发布脚本等)
- 主项目只保存了子模块的链接,说明了当前版本的主项目依赖那个版本的子项目,那么需要正确对这些依赖关系进行管理,而这不太容易
- 发布时需要自己处理依赖关系,先发子项目,再发主项目
对于我们来说,monorepo
最大的问题有两个:
- 仓库体积巨大,并且需要将现有的若干个仓库整合为一个仓库,工作量巨大
- 如果要使用这种方案,需要使用
yarn
的workspaces
功能和lerna
,增加了对工具和相关配置的学习成本。
而针对 Git 子模块的三个缺点:
- 我们现在其实已经是每个子模块都有独立的仓库和代码规范、发布脚本等,对于我们来说不算缺点,而是优点
- 对于处理依赖关系和发布顺序,可以开发响应的命令行工具,以更友好的方式对开发人员进行约束和辅助(类似于使用
git cz / trs tag
等工具),学习成本也不高。
所以,最终我们选用 Git 子模块方案来解决我们的问题
3. 方案实施示范
这里为了方便演示,我将采编模块作为主仓库,用绩效考核模块来同时模拟子模块和公共模块,来示范整个方案的实施过程。
3.1 添加子模块
在添加子模块之前,要想确保子模块代码不被提交到主模块仓库中去,需要先将子模块目录添加到.gitignore
文件中;
// .gitignore
/performance
首先,我们在采编模块根目录下,执行命令:
git submodule add https://git.trscd.com.cn/cdtrs/dev/03_super_star/tianMuYun/performance
执行成功后,我们使用 git status
查看当前目录状态,发现:
- 多了
performance
目录 - 多了一个
.gitmodules
文件
performance
目录下就是我们的绩效考核的代码,而.gitmodules
文件内容为:
[submodule "performance"]
path = performance
url = https://git.trscd.com.cn/cdtrs/dev/03_super_star/tianMuYun/performance
这里存放了对子模块的引用信息。
3.2 在子模块中新建用来共享的测试组件
我们在performance/src/coomponent/normal
目录下新建一个用来测试共享的组件SubCom.vue
:
<template>
<div>
<span>这是来自子模块的组件,作者为 {{ name }}</span>
</div>
</template>
<script>
export default {
name: 'SubCom',
data() {
return {
name: 'lll',
};
},
};
</script>
3.3 使用子模块内的组件
我们在采编头条号模块的基座应用(src/views/editingCenter/components/newMediaNewsList/Index.vue
)中引入子模块的组件:
<template>
...
<SubCom></SubCom>
...
</template>
<script>
import SubCom from '../../../../../src/components/normal/SubCom.vue';
//...
export default {
components: {
//...
SubCom,
},
}
</script>
现在来看看效果:
3.4 子模块别名设置
你一定注意到了上面代码中 import SubCom from '../../../../../src/components/normal/SubCom.vue'
的这一长串代码,我们采用了相对路径去引用子模块组件,非常难受。
我们可以通过在主模块webpack配置中为子模块路径设置别名来解决:
// 主模块vue.config.js
configureWebpack: {
//...
resolve: {
alias: {
Performance: path.resolve(__dirname, 'performance'),
},
},
},
然后这样引用:
import SubCom from 'Performance/src/components/normal/SubCom.vue';
3.5 子模块改动提交
切换到performance
目录,进行提交:
$ git add .
$ git commit -m "添加Git子模块测试组件"
[submoduleTest 07384a1] 添加Git子模块测试组件
1 file changed, 16 insertions(+)
create mode 100644 src/components/normal/SubCom.vue
3.6 主模块改动提交
切换到根目录,查询状态:
$ git status
On branch subModel
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: .gitmodules
new file: performance
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: performance (new commits)
modified: src/views/editingCenter/components/newMediaNewsList/Index.vue
modified: vue.config.js
我们注意到两点情况:
- 一般情况下,当我们在Git仓库目录中新增一个目录时,它显示的是一个目录形式的路径,就像下面:
$ git status
On branch subModel
Untracked files:
(use "git add <file>..." to include in what will be committed)
test/
而这里,我们的performance
目录 被识别为一个文件new file: performance
;
- 在已修改文件列表里,我们也看到了
performance (new commits)
这条记录
这就是Git 子模块的机制,它实际上只是存储了我们子模块仓库的一个索引,所以它表现为一个文件,而子模块目录里的文件不会被提交到主模块仓库中,它会将子模块当前分支最新提交作为一个提交记录,提交到主模块去。
我们验证一下:
$ git add .
$ git commit -m "增加绩效考核子模块"
[subModel 4113e12] 增加绩效考核子模块
4 files changed, 14 insertions(+), 12 deletions(-)
create mode 100644 .gitmodules
create mode 160000 performance
$ git push origin subModel
看一下线上的Git仓库:
当点击这个文件时,就会跳转到子模块所在仓库。
而这个文件后面跟的 @54d73859
就是主模块所引用的子模块的那个提交的HASH值,我们切换到子模块目录验证:
$ git log
commit 54d73859763e2b4c87f623631ac29b9821ef075d (HEAD -> subModel)
Author: hjb2722404 <hjb2722404@163.com>
Date: Tue Jan 11 09:49:46 2022 +0800
添加Git子模块测试组件
4. 总结
通过这样的方式,我们就可以同时实现子应用与主应用统一管理但又能分开管理,并且各个模块之间还可以共享组件和依赖的需求。总的结构如下:
当然,这里为了方便,将公共模块与各个子模块并列放在了一起,实际上,公共模块同样可以作为各个子模块的子模块还使用,也就是说,子模块时可以嵌套的。具体以实际需求和应用场景为准。
更多推荐
所有评论(0)