微前端
微前端
微前端的概念由ThoughtWorks提出的,利用微件拆分来达到工程拆分治理的方案,可以解决工程膨胀、开发维护困难等。
传统Web应用研发模式
-
将平台内多个系统放置同一个代码仓库维护 ,采用 SPA(Single-page Application) 单页应用模式
-
优点:天生具备体验上的优势,应用直接无刷新切换,能极大的保证多产品之间流程操作串联时的流程性。
-
缺点: 在于各应用技术栈之间是强耦合的。
-
-
将系统分为多个仓库维护,在首页聚合所有平台的入口,采用 MPA(Multi-page Application)多页应用模式
-
优点:在于部署简单、各应用之间硬隔离,天生具备技术栈无关、独立开发、独立部署的特性。
-
缺点: 应用之间切换会造成浏览器重刷,由于产品域名之间相互跳转,流程体验上会存在断点,卡顿、闪烁问题。
-
微前端架构应用场景
-
兼容遗留系统,需使用新框架去开发新功能,没必要花费精力去重构旧系统;
-
局部/增量升级,项目结构难以管控,开发、构建、部署变得越来越慢,开发体验持续下降;
-
区别交付,即以功能插件为单位,给不同的客户交付不同的插件,满足一次开发多端交付;
-
应用聚合,财务、考勤和项目系统分散且管理成本高,基于vue、react、JQuery的系统,将多种类型的系统聚合为一个系统;
-
团队间共享,跨团队开发,采用组件或服务的方式进行团队间的技术共享。其低内聚高耦合的共享,使得高质量的共享成为可能;
-
技术驱动,使用新技术开发新功能。
微前端架构的优点
-
技术栈无关,可以与时俱进,不断引入新技术/新框架
前端技术栈日新月异,推陈出新的速度绝对是冠绝群雄;如何在维护好遗留系统的前提下,不断引入新技术和新框架,提高开发效率、质量、用户体验,成为每个团队需要认真对待的问题。微前端可以很好的实现应用和服务的隔离,互相之间几乎没有影响,可以很好的支持团队引入新技术和新框架, 主框架不限制接入应用的技术栈,子应用具备完全自主权。
-
独立开发、测试和部署
就像微服务一样,微前端的一大优势就是可独立部署的能力。每个子应用各自管理自己的开发,测试和部署,几乎不用考虑其他代码库或管道的状态;就算旧的单体架构采用了固定、手动的发布周期,或者隔壁的团队在他们的主分支里塞进了一个半成品或失败的功能,也不影响我们的工作。如果某个微前端已准备好投入生产,那么它就能顺利变为产品,且这一过程完全由开发和维护它的团队主导、子应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新。
-
单一职责、独立运行时
状态隔离,独立运行时,每个子应用之间状态隔离,运行时状态不共享。
-
用户体验好
应用之间切换流畅,高内聚的系统,不需要频繁的切换系统去处理业务。
微前端架构的缺点
-
重复依赖
不同应用之间依赖的包存在很多重复,由于各应用独立开发、编译和发布,难免会存在重复依赖的情况。导致不同应用之间需要重复下载依赖,额外增加了流量和服务端压力。
-
复杂度高
拆分的粒度越小,便意味着架构变得复杂、维护成本变高,技术栈一旦多样化,便意味着技术栈混乱。
常见微前端解决方案
前端容器化,借助Iframe
应用组件化,Web Components模式
基座模式,主要基于路由分发,single-spa和qiankun就是基于这种模式
EMP模式,主要基于Webpack5 Module Federation
组合式集成,即单独构建组件,按需加载,类似npm包的形式
1、
众所周知,
iframe 与微前端概念中提出的独立开发、独立维护、相互隔离非常的吻合,但 iframe 真的是最佳微前端解决方案吗?事实并非如此。《
缺点:
-
URL 不同步,浏览器刷新 Iframe URL状态丢失、后退前进按钮无法使用(缓存解决)。
-
UI不共享,DOM结构不共享,屏幕3/4的iframe带遮罩层的对话框,如果让它基于整个页面自适应垂直水平居中(无法解决)。
-
全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登录效果。还有搜索引擎无法获取到其中的内容,无法实现应用的 seo等(几乎无法解决)。
-
慢,每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。
iframe 实现的微前端解决方案很难满足现代化业务开发场景。
2、
Web Components 是由 google 推出的原生组件,MDN 对其定义是这样的:
作为开发者,我们都知道尽可能多的重用代码是一个好主意。这对于自定义标记结构来说通常不是那么容易 — 想想复杂的 HTML(以及相关的样式和脚本),有时您不得不写代码来呈现自定义 UI 控件,并且如果您不小心的话,多次使用它们会使您的页面变得一团糟。
Web Components 旨在解决这些问题 — 它由三项主要技术组成,它们可以一起使用来创建封装功能的定制元素,可以在你喜欢的任何地方重用,不必担心代码冲突。
三项主要技术是指:
Custom elements(自定义元素) :一组 JavaScript API,允许开发者定义 custom elements 及其行为,然后可以在用户界面中按照需要使用它们。
Shadow DOM(影子DOM):一组 JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
HTML templates(HTML模板) : <template>
和 <slot>
元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。
结合微前端的概念,Web components 在某些方面做到了微前端:
-
技术栈无关:Web Components 是浏览器原生组件,那即是在任何框架中都可以使用。
-
独立开发:使用 Web Components 开发的应用无需与其他应用间产生任何关联。
-
应用间隔离:Shadow DOM 的特性,各个引入的微应用间可以达到相互隔离的效果。
缺点:
重写现有的前端应用。我们需要使用Web Components来完成整个系统的功能。
上下游生态系统不完善。缺乏相应的一些第三方控件支持。
系统架构复杂。当应用被拆分为一个又一个的组件。
可以利用它的特性创建可重用的定制元素(自定义style、script等),有点像可定制的Iframe,新特性兼容性不好
3、
全异步编程,对于用户需要提供的 load,bootstrap,mount,unmount 均使用 promise 异步的形式处理,不管同步、异步都能支持
通过劫持路由,可以在每次路由变更时先判断是否需要切换应用,再交给子应用去响应路由标准化每个应用的挂载和卸载函数(监听popstate或者hashchange,自定义路由分发机制),
不耦合任何框架,只要子应用实现了对应接口即可接入系统中
导航路由+资源加载框架
-
load 方法需要知道子项目的入口文件 (应用加载,启动,挂载和卸载等逻辑)
-
把多个应用的运行时集成起来需要项目间自行处理内存泄漏,样式污染问题
-
没有提供父子数据通信的方式
4、
导航路由+资源加载框架 :qiankun是基于
-
路由匹配机制 :首先由主应用的路由匹配到某个子应用,框架先加载 entry 资源,待 entry 资源加载完毕,确保子应用的路由系统注册到主应用之后,再由子应用的路由系统接管 url change 事件。同时在子应用切出时,主应用应触发响应的 destroy 事件,子应用在监听到该事件后,调用自己的卸载方法卸载应用。qiankun 直接使用了社区中较为完善的 single-spa 方案。
-
打包形式 :qiankun 的子应用打包形式,采用的是运行时构建。在大型中后台应用开发中,独立的模块化管理显然更有价值、更值得去关注。
-
资源加载方式 :qiankun 提供了 JS Entry 和 HTML Entry 两种子应用资源加载方式,但通常在实际项目中我们更多的是采用 HTML Entry 加载资源。
-
样式隔离 :业内实现应用样式隔离的技术方案有 Shadow DOM、BEM、Dynamic Stylesheet 等,qiankun 选用动态样式表作为样式隔离解决方案,结合 HTML Entry 天生具备样式隔离的特性,能够较好地在架构层面确保应用的样式不会出现相互干扰的问题。
-
JS 隔离 :qiankun 独创了运行时 JS 沙箱,即在应用的 bootstrap 及 mount 两个生命周期开始之前分别给全局状态打下快照,然后当应用切出/卸载时,将状态回滚至 bootstrap 开始之前的阶段,确保应用对全局状态的污染全部清零。而当应用二次进入时则再恢复至 mount 前的状态的,从而确保应用在 remount 时拥有跟第一次 mount 时一致的全局上下文。另外,沙箱还对一些全局事件监听的劫持等,以确保应用在切出之后,对全局事件的监听能得到完整的卸载,同时也会在 remount 时重新监听这些全局事件,从而模拟出与应用独立运行时一致的沙箱环境。qiankun 引入的沙箱机制,有效的避免了全局变量污染和内存泄露问题。
-
父子应用通信 :qiankun 实现了一套简单可用的全局数据存储,他作为 single-spa 事件的补充,父子应用均可以工读这个存储的数据。
-
资源预请求 :预请求充分利用了
importEntry
把获取资源和执行资源分离的点来提前加载所有子应用的资源。
5、
webpack5 发布之初,其 module federation 特性就与微前端联系到一起(让代码直接在项目间利用 CDN 直接共享)。EMP 是由欢聚时代前端团队自研的单页微前端解决方案,它利用了 webpack5 module federation 新特性,实现了跨组件模块调用方式【欢聚时代团队】。
-
基于 Webpack5 的新特性 Module Federation 实现,达到第三方依赖共享,减少不必要的代码引入的目的。
-
每个微应用独立部署运行,并通过 cdn 的方式引入主程序中,因此只需要部署一次,便可以提供给任何基于 Module Federation 的应用使用。并且此部分代码是远程引入,无需参与应用的打包。
-
动态更新微应用:EMP 是通过 cdn 加载微应用,因此每个微应用中的代码有变动时,无需重新打包发布新的整合应用便能加载到最新的微应用。
-
去中心化,每个微应用间都可以引入其他的微应用,无中心应用的概念。
-
跨技术栈组件式调用,提供了在主应用框架中可以调用其他框架组件的能力。
-
按需加载,开发者可以选择只加载微应用中需要的部分,而不是强制只能将整个应用全部加载。
-
应用间通信,每一个应用都可以进行状态共享,就像在使用 npm 模块进行开发一样便捷。
-
生成对应技术栈模板,它能像 create-react-app 一样,也能像 create-vue-app 一样,通过指令一键搭建好开发环境,减少开发者的负担。
EMP 微前端方案除了具备微前端的能力外,它还实现了跨框架组件调用的能力
6、其他
其他
微前端发展至今,已经不再是一门具体的技术,而是整合了技术、策略和方法,可能会以脚手架、辅助插件和规范约束这种生态圈形式展示出来,是一种宏观上的架构。每一套微前端方案的设计,都是基于实际需求出发。我们要做的就是形成适合项目研发的微前端生态。
除了微前端解决方案选型之外,我们可能还需要考虑一些微前端相关的周边问题,包括:
-
微前端周边脚手架:现在大多数开源的微前端框架都配备了脚手架工具,我们也可以选择内部定制。
-
微前端项目配套校验库:微前端对应于解耦后,多个子项目的管理也是个不得不考虑的问题,校验库此类配套设施,应根据项目具体定制。
-
微前端项目配套 git 工作流:微前端作为一种架构设计,会带来工作流的改变,如统一的版本发布、交付等。
-
微前端自研构建工具(可选):构建工具受技术栈的限制,我们可选的官方方案只有 vue-cli,这是一个不错的选择,但不一定是最适合项目的选择,在 vue-cli 基础上再开发或是完全自研一套构建方案,这是值得考虑的问题。