一. 了解 微前端
(1) 解决的问题
- 工程越来越大,打包越来越慢
- 团队人员多,产品功能复杂,代码冲突频繁、影响面大
- 内心想做 SaaS 产品,但客户总是要做定制化
如图所示,就可以看出微前端的好处 - 应用自治。只需要遵循统一的接口规范或者框架,以便于系统集成到一起,相互之间是不存在依赖关系的。
- 单一职责。每个前端应用可以只关注于自己所需要完成的功能。
- 技术栈无关。你可以使用 Angular 的同时,又可以使用 React 和 Vue。
(2) 微前端
那什么是微前端?微前端主要是借鉴后端微服务的概念。简单地说,就是将一个巨无霸(Monolith)的前端工程拆分成一个一个的小工程。应用可以独立运行、独立开发、独立部署。以及,它们应该可以在共享组件的同时进行并行开发——这些组件可以通过 NPM 或者 Git Tag、Git Submodule 来管理。别小看这些小工程,它们也是“麻雀虽小,五脏俱全”,完全具备独立的开发、运行能力。整个系统就将由这些小工程协同合作,实现所有页面的展示与交互。
这里要注意跟 iframe 实现页面嵌入机制的区别。微前端没有用到 iframe,它很纯粹地利用 JavaScript、MVVM 等技术来实现页面加载。后面我们将介绍相关的技术实现。
(3) 如果实现微前端
可以将一个应用划分成多个子应用,将子应用打包成一个个模块。当路径切换时加载不同的子应用。这样每个子应用都是独立的,技术栈也不用做限制。从而解决前端协同开发问题。(子应用需要暴露固定的钩子 mount,unmount 等生命周期)
A. 路由分发式微前端
路由分发式微前端,即通过路由将不同的业务分发到不同的、独立前端应用上。其通常可以通过 HTTP 服务器的反向代理来实现,又或者是应用框架自带的路由来解决。
目前,通过路由分发式的微前端架构应该是采用最多、最容易实现的 “微前端” 方案。但这种方式实际上只是多个前端应用的聚合并不是一个完整的整体。每次用户从A应用跳转到B应用的时候,实际上是访问不同的html页面。
它适用于以下场景:
- 不同技术栈之间差异比较大,难以兼容、迁移、改造
- 项目不想花费大量的时间在这个系统的改造上
- 现有的系统在未来将会被取代
- 系统功能已经很完善,基本不会有新需求
B.使用iFrame创建容器
使用iframe标签嵌套将另一个HTML页面嵌入到当前页面中。iframe可以创建一个全新的独立的宿主环境,这意味着我们的前端应用之间可以相互独立运行。
采用iframe有几个重要的前提:
- 网站不需要 SEO 支持
- 拥有相应的应用管理机制。
在采用iframe的时候,我们需要做这么两件事:
- 设计管理应用机制:在什么情况下,我们会去加载、卸载这些应用;在这个过程中,采用怎样的动画过渡,让用户看起来更加自然。
- 设计应用通讯机制:直接在每个应用中创建 postMessage 事件并监听,并不是一个友好的事情。其本身对于应用的侵入性太强,因此通过 iframeEl.contentWindow去获取iFrame容器内的Window对象是一个更简化的做法。随后,就需要定义一套通讯规范:事件名采用什么格式、什么时候开始监听事件等等。
C.Web Components技术构建
Web Components是一套不同的技术,允许您创建可重用的定制元素(它们的功能封装在您的代码之外)并且在您的 Web 应用中使用它们。
一个典型的Web Components应用架构如下图所示:
可以看到这边方式与我们上面使用iframe的方式很相似,组件拥有自己独立的 Scripts 和 Styles,以及对应的用于单独部署组件的域名。然而它并没有想象中的那么美好,要直接使用纯Web Components来构建前端应用的难度有:
- 重写现有的前端应用。我们需要使用Web Components来完成整个系统的功能。
- 上下游生态系统不完善。缺乏相应的一些第三方控件支持,这也是为什么jQuery相当流行的原因。
- 系统架构复杂。当应用被拆分为一个又一个的组件时,组件间的通讯就成了一个特别大的麻烦。
Web Components中的Shadow DOM更像是新一代的前端DOM容器,遗憾的是并不是所有的浏览器,都可以完全支持Web Components。在 Chrome 和 Opera 浏览器上,对于 Web Components 支持良好,而对于 Safari、IE、Firefox 浏览器的支持程度,并没有那么理想。
D.Singe-SPA
2018年Singe-SPA 诞生,这个用于前端微服务化的JavaScript前端解决方案实现了路由劫持和应用加载,缺点:没样式隔离,js执行隔离
E.qiankun
2019年qiankun基于Singe-SPA提供了更加开箱即用的API(Singe-SPA +sandbox + import-html-entry)做到了,技术栈无关并且接入简单(像iframe一样简单)
缺点:比如公共模块加载,模块通信处理并不是很好,同时再使用的时候也会需要要改下原项目中的代码 暴露方法
F.EMP
2020年EMP基于module federation(webpack5),接入成本低,解决第三方依赖包问题
(4) 缺点
有得必有失,在引入微前端的过程中难免会引入一些新的问题。只不过我们认为这些风险都能控制在合理水平上,微前端终究还是利大于弊的。
A . 重复依赖
不同应用之间依赖的包存在很多重复,由于各应用独立开发、编译和发布,难免会存在重复依赖的情况。导致不同应用之间需要重复下载依赖,额外再增加了流量和服务端压力。
B. 团队之间更加分裂
大幅提升的团队自治水平可能会让各个团队的工作愈加分裂。各团队只关注自己的业务或者平台功能,在面向用户的整体交付方面,会导致对用户需求和体现不敏感,和响应不及时。
C.拆分越细,维护成本越高
拆分的粒度越小,便意味着架构变得复杂、维护成本变高。
D.技术栈混乱
技术栈一旦多样化,便意味着技术栈混乱。
(5)微前端应用场景
A. 兼容遗留系统
经常会有团队需要在兼容已有系统的前提下,使用新框架去开发新功能。遗留系统功能已经完善,并且稳定运行,团队没有必要,也没有精力去将遗留系统重构一遍。
B. 应用聚合
大型互联网公司都会为用户提供很多应用和服务,如何为用户呈现具有统一用户体验的应用聚合成为必须解决的问题。而在大型商业公司内部,往往部署有大量的软件服务。如何为员工提供服务聚合,提供员工工作效率,成为企业内部IT建设的重中之重。前端聚合已成为一个技术趋势,目前比较理想的解决方案就是微前端。
C.团队间共享
不用应用之间往往存在很多可以共享和功能和服务,如果在团队之间进行高质量的共享成为提高研发效率的一条重要途径。微前端可以采用组件或者服务的方式进行团队间的技术共享。其低内聚高耦合的共享,使得高质量的共享成为可能。
D.局部/增量升级
一个大的产品由很多应用和服务组成,很多时候只需要对部分应用和服务进行升级。如果是单体应用,升级耗时长,风险高,影响可服务性。而前端可以只对需要的应用和服务进行升级,不会影响其他应用和服务。升级效率高,风险低,不影响其他应用和服务的可服务性。
(6)要克服的几个障碍
A.资源的隔离
由于存在不同应用各自定义CSS和全局变量的情况,应用聚合时需要考虑彼此之间的影响。应用JS沙箱和CSS隔离等相关技术,使各应用之间互不影响。
B.应用的注册
Html Entry 和 Config Entry,是关于如何注册子应用信息。
C.对性能的影响
按需加载、公共依赖加载和预加载,是关于性能的,这些很重要,否则虽然上了微前端,但性能严重下降,或者由于升级引起线上故障,就得不偿失了。
D.应用间通信
父子应用通讯
E.应用嵌套/并行
子应用嵌套 和 子应用并行 是微前端的进阶应用,在某些场景下会用到。
(7)微前端的业务拆分
- 按照业务拆分
- 按照权限拆分
- 按照变更频率拆分
- 按照组织结构拆分
- 跟随后端微服务拆分
如何选择,就要看实际情况了
二. 微前端设计
遵循单一职责和复用原则,按照领域模型和微服务边界,将前端页面
进行拆分。同时构建多个可以独立部署、完全自治、松耦合的页面组合,其中每个组合只负
责特定业务单元的 UI 元素和功能,这些页面组合就是微前端。
(1)架构模式
微前端应用间的关系来看,分为两种**:基座模式(管理式)、自组织式**。分别也对应了两者不同的架构模式:
- 基座模式。通过一个主应用,来管理其它应用。设计难度小,方便实践,但是通用度低。
- 自组织模式。应用之间是平等的,不存在相互管理的模式。设计难度大,不方便实施,但是通用度高。
就当前而言,基座模式实施起来比较方便,方案上便也是蛮多的。
而不论种方式,都需要提供一个查找应用的机制,在微前端中称为服务的注册表模式。和微服务架构相似,不论是哪种微前端方式,也都需要有一个应用注册表的服务,它可以是一个固定值的配置文件,如 JSON 文件,又或者是一个可动态更新的配置,又或者是一种动态的服务。它主要做这么一些内容: - 应用发现。让主应用可以寻找到其它应用。
- 应用注册。即提供新的微前端应用,向应用注册表注册的功能
- 第三方应用注册。即让第三方应用,可以接入到系统中。
- 访问权限等相关配置。
应用在部署的时候,便可以在注册表服务中注册。如果是基于注册表来管理应用,那么使用基座模式来开发比较方便。
(2)设计理念
以下几点是我们在设计的过程中,需要关注的内容:
- 中心化:应用注册表。这个应用注册表拥有每个应用及对应的入口。在前端领域里,入口的直接表现形式可以是路由,又或者对应的应用映射。
- 标识化应用。我们需要一个标识符来标识不同的应用,以便于在安装、卸载的时候,能寻找到指定的应用。一个简单的模式,就是通过康威定律来命名应用。
- 应用生命周期管理。
- 高内聚,低耦合。
(3)生命周期
前端微架构与后端微架构的最大不同之处,也在于此——生命周期。微前端应用作为一个客户端应用,每个应用都拥有自己的生命周期(暴露生命周期,方便再主应用调用):
- Load,决定加载哪个应用,并绑定生命周期
- bootstrap,获取静态资源。
- Mount,安装应用,如创建 DOM 节点。
- Unload,删除应用的生命周期。
- Unmount,卸载应用,如删除 DOM 节点、取消事件绑定。
这部分的内容事实上,也就是微前端的一个难点所在,如何以合适的方式来加载应用——毕竟每个前端框架都各自不同,其所需要的加载方式也是不同的。当我们决定支持多个框架的时候,便需要在这一部分进入更细致的研究。
三. 补充
(1) systemJS
systemJS 是一个通用的模块加载器,它能在浏览器上动态加载模块。微前端的核心就是加载微应用,将应用打包成模块,在浏览器中通过systemJS来加载模块。(注意:使用Singe-SPA 就必须要用到systemJS)
system.import(’./index.js’) //动态加载js,也可以加载远程连接
(2)qiankun
qiankun 是蚂蚁金服开源的一套完整的微前端解决方案。乾坤是一个阿里巴巴UMI基于single-spa
四. 材料
Module Federation原理剖析
这才是最完美的微前端方案
实施微前端的六种方式
微前端简介
微前端的设计理念与实践初探
微前端如何落地
从0实现微前端