这里是突然勤奋的 Zhuo,持续更新文档中。
本文只涉及到 module-federation
微前端方案的设计层面分享,关于具体实现,可以参考写的简单例子;所有的设计图自取,如果对你有一些启发,请点个 star
背景
团队从瀑布流模式转变为敏捷模式,将整个项目团队划分为多个小组并行开发,多小组同时升级的分支处理和代码冲突之类的问题频繁出现。于是接到了组内一个技术需求:《模块支持单独升级和部署》。目前,我们开发和维护代码的现状:
- 一个仓库包含了所有的业务组的前端代码
- 只能一起打包出一个整包,也无法单独部署
- 后端已完成微服务的拆分
如上所述,虽然一般场景通过 git 多分支管理能解决【独立开发】和【发布】的问题,但是一遇到【多团队同时发布】的场景就让人头疼了:
- 需要拉上各个团队的负责人,反复确认当天能否正常上线
- 上线分支排列组合(release-a、release-b、release-ab )预防某一个团队突发问题不上线
- 测试工作量增加:原本只要验收一个代码包,现在还要验证多个代码包功能(> 1)
看一眼分支模型:
既然提出了已有问题,为了解决这些问题,我们意识到和后端一样将整个项目拆分成若干的子应用,将它们也变成微服务,不就可以解决目前的困境了吗?自然而然我们就想到了“微前端”的概念。有目标,就好发力了,我们参考了业务优秀方案,同时结合了自身实际情况,最终决定用webpack5 module federation
特性来进行微前端的实践与落地。
需求分析
外部需求
- 拆分解耦
- 单独打包,独立部署
- 不影响现在的用户体验
内部需求
- 不影响开发体验
- 方案对原业务代码侵入性最低
- 学习成本低
- 统一技术栈
- 前端灰度发布
方案选择
方案:
-
应用微服务化,诸如 qiankun2
- 存在性能问题,且沙箱逃逸问题难解决,放弃
-
微件化:通过组合多个独立应用、组件来构建一个单体应用,例如 Module Federation
- 在接入第三方应用上存在的问题比较大,但是对于我们想要达到【独立升级】的效果来说,此短板可以接受
-
结合 Web Components 构建,例如 micro-app
- 在处理沙箱的性能问题和逃逸问题上虽然比方案1更优秀,但这两个问题出现还是难解决,且目前官方维护的频率及未有稳定版本,放弃
-
MPA+路由分发
- 相当将原本的一个系统拆成多个系统,体验上无法接受,放弃
-
MPA + iframe
- iframe 在性能和用户体验上的问题也是无法接受,放弃
-
纯 Web Component 进行构建应用
- 需要用一个新的基础方案将整个项目的代码重构,工作量上无法接受,放弃
最终采用方案2:module federation
。
微前端实践
通过方案的分析以及项目实际情况的梳理,我们确定了微前端的整体方案,输出组件图,如下图所示:
可以看到,整个方案非常简单:按照开发小组进行路由级别的拆分,整个系统可分为两个部分:
- 基座应用:用于管理子应用的路由切换、注册子应用的路由和全局 Store 层、提供全局库和复用层
- 子应用:用于开发子应用业务代码,一个子应用对应一个开发小组,并且管理自己的路由、语言包、埋点、Store
基座应用和子应用联系起来的桥梁则是入口文件:remoteEntry.js
。这可以让基座应用准确地发现子应用资源的路径从而进行加载。
因为依赖了remoteEntry.js
进行关联,所以这个文件不会加 hash 后缀,这就要求对该命名的文件全部设置协商缓存,不再是强缓存。
微前端下的架构变化
在整个方案的改造之后,我们的项目整体的架构变成了如下图展示的样子:
前端侧的代码按照小组特性拆分成若干个子应用:
- 每个子应用只拥有自己相关的代码,基座应用可以看成比较特殊的子应用,它提供工程能力,最好不要有业务代码
- 将应用代码划分到
packages/apps
文件下, 通过monorepo + pnpm
进行管理依赖 - 所有的文件夹进行单独的打包构建,搭配
Docker + Kubernetes
构建成单独的容器(红色部分内所有的子应用都是单独的容器)变成真正的微服务 - 通过
ingressroute + nginx
加载不同容器之间的子应用资源
以上的变化,让拆分出来的子应用能够独立开发 - 维护 - 发布之外,还使得我们前端应用升级时的 灰度 - 部署 - 回滚 操作变得更加简单、快速。
部署方案
在达成独立部署上线的目标之后,所有的子应用都是独立的,发布不需要依赖于任何应用。只需要确认开发修改了哪些子应用的代码,那么就升级对应的子应用即可。
具体流程:
- 开发修改了 A 应用的代码,上库
- 打包平台(千流,公司自建)识别到具体变更的代码路径,对 A 应用进行打包,生成镜像容器、版本号等,推送到镜像平台
- 升级对应服务
虽然步骤是这些,但是在开发人员这里,他只需要提供本次升级的版本号(1.0.xxx)即可,背后流程不需要深入了解。
回滚方案
得益于 Docker + Kubernets
,前端的回滚只需要执行一条 helm
命令即可。除此没有其他的内容。
总结
总的来说,我们完成了以下的目标:
- [x] 代码拆分解耦
- [x] 单独打包,独立部署
- [x] 不影响现在的用户体验:没有引入其他类库,也没有使用 iframe
- [x] 不影响开发体验:支持热更新
- [x] 方案对原业务代码侵入性最低:核心工作量在代码拆分,webpack的配置并没有多少工作量
- [x] 学习成本低:基本没有学习成本
- [x] 统一技术栈:本来就是一个项目
- [x] 前端灰度发布
学习链接
感谢以下文章的作者/团队分享的文章,提供了很好的思路。
- Module federation 原理研究
- Revolutionizing Micro Frontends with Webpack 5, Module Federation and Bit
- How to Build a Micro Frontend with Webpack's Module Federation Plugin
- Webpack Module Federation
- Module Federation Examples
- https://medium.com/@A__G__B/i...
- EMP-面向未来微前端方案正式开源了!
- micro-app 介绍
- 可能是你见过最完善的微前端解决方案
- 字节跳动是如何落地微前端的
- 微前端在美团外卖的实践
都看到这里了,帮忙点个赞呗~