基于Git子模块的微前端项目管理和公用组件库方案

基于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最大的问题有两个:

  • 仓库体积巨大,并且需要将现有的若干个仓库整合为一个仓库,工作量巨大
  • 如果要使用这种方案,需要使用yarnworkspaces功能和lerna,增加了对工具和相关配置的学习成本。

而针对 Git 子模块的三个缺点:

  1. 我们现在其实已经是每个子模块都有独立的仓库和代码规范、发布脚本等,对于我们来说不算缺点,而是优点
  2. 对于处理依赖关系和发布顺序,可以开发响应的命令行工具,以更友好的方式对开发人员进行约束和辅助(类似于使用 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

我们注意到两点情况:

  1. 一般情况下,当我们在Git仓库目录中新增一个目录时,它显示的是一个目录形式的路径,就像下面:
	$ git status
	
	On branch subModel
	Untracked files:
	  (use "git add <file>..." to include in what will be committed)
			test/

而这里,我们的performance 目录 被识别为一个文件new file: performance;

  1. 在已修改文件列表里,我们也看到了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
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值