微前端技术选型
微前端是一种利用微件拆分来达到工程拆分治理的方案,可以解决工程膨胀、开发维护困难等问题。
随着前端业务场景越来越复杂,微前端这个概念最近被提起得越来越多,业界也有很多团队开始探索实践并在业务中进行了落地。可以看到,很多团队也遇到了各种各样的问题,但各自也都有着不同的处理方案。
本次调研微前端实现方案,旨在对比业界知名的微前端解决方案,分析这些各方案的利弊之处,来帮助我们前端团队确认一种或多种稳定、高效、可行的微前端落地方案。
微前端架构在公司项目的应用场景
目前公司内部使用微前端架构的项目越来越多,其大多数是中后台项目。这些使用微前端架构的项目,它们的共同特点包括但不限于以下几点:
- 业务发展迅速,新业务线新功能随时增加,导致应用不断膨胀,甚至可能出现巨石应用;
- 项目文件越来越多,文件结构难以管控,开发、构建、部署变得越来越慢,开发体验持续下降;
- 业务需要区别交付,即以功能插件为单位,给不同的客户交付不同的插件,满足一次开发多端交付;
- 多条业务线并存,比如 PC 系统、H5 系统等,导致功能分散且管理成本变高;
- 跨团队开发、跨技术栈开发等。
- ...
微前端架构给项目带来的价值
微前端架构设计具备以下几个核心价值:
- 技术栈无关 主框架不限制接入应用的技术栈,子应用具备完全自主权
- 独立开发、独立部署 子应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新
- 状态隔离 独立运行时,每个子应用之间状态隔离,运行时状态不共享
其中,我们的项目大多是使用 vue2 开发,所以技术栈无关在我们团队中应用场景较少。而独立开发、独立构建、独立部署、状态隔离是我们所需要的,这些特性给我们的项目开发体验、开发效率带来很大的提升。
业内现有微前端解决方案
从技术实现角度,微前端架构解决方案大概分为两类场景:
- 单实例:即同一时刻,只有一个子应用被展示,子应用具备一个完整的应用生命周期。通常基于 url 的变化来做子应用的切换。
- 多实例:同一时刻可展示多个子应用。通常使用 Web Components 方案来做子应用封装,子应用更像是一个业务组件而不是应用。
现有的微前端解决方案有以下几种:
- iframe
- Web Components
- single-spa (定位:导航路由+资源加载框架)
- qiankun ( 基于 single-spa,由蚂蚁团队开源)
- Alfa (由阿里云团队开源)
- EMP (由欢聚时代团队开源,基于 webpack5 module federation 实现,概念先进)
iframe
众所周知,iframe 是 html 提供的内联框架元素,它能够将另一个 HTML 页面嵌入到当前页面中,并且能兼容所有浏览器。因此在微前端概念被提出后,iframe 成为最早的微前端解决方案,你可以用它来加载任何你想要加载的 web 应用。
iframe 最大的特性就是提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离这类问题统统都能被完美解决。iframe 与微前端概念中提出的独立开发、独立维护、相互隔离非常的吻合,但 iframe 真的是最佳微前端解决方案吗?事实并非如此。Why Not Iframe 文中提到,iframe 最大的问题也在于他的隔离性无法被突破,导致应用间上下文无法被共享,随之带来的开发体验、产品体验的问题。
iframe 作为微前端解决方案存在的问题有:
- url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
- UI 不同步,DOM 结构不共享。想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中。
- 全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。还有搜索引擎无法获取到其中的内容,进而无法实现应用的 seo。
- 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。
其中有的问题比较好解决(问题 1),有的问题我们可以睁一只眼闭一只眼(问题 4),但有的问题我们则很难解决(问题 3)甚至无法解决(问题 2),而这些无法解决的问题恰恰又会给产品带来非常严重的体验问题。基于 iframe 实现的微前端解决方案很难满足现代化业务开发场景,因此这种方案注定被舍弃。
之前莫比乌斯前端团队有基于 iframe 的实践,后来使用体验更好的 single-spa 重构。
Web Components
Web Components 是由 google 推出的原生组件,MDN 对其定义是这样的:
作为开发者,我们都知道尽可能多的重用代码是一个好主意。这对于自定义标记结构来说通常不是那么容易 — 想想复杂的 HTML(以及相关的样式和脚本),有时您不得不写代码来呈现自定义 UI 控件,并且如果您不小心的话,多次使用它们会使您的页面变得一团糟。
Web Components 旨在解决这些问题 — 它由三项主要技术组成,它们可以一起使用来创建封装功能的定制元素,可以在你喜欢的任何地方重用,不必担心代码冲突。
其中,三项主要技术是指:
- Custom elements:一组 JavaScript API,允许开发者定义 custom elements 及其行为,然后可以在用户界面中按照需要使用它们。
- Shadow DOM:一组 JavaScript API,用于将封装的“影子”DOM 树附加到元素(与主文档 DOM 分开呈现)并控制其关联的功能。通过这种方式,您可以保持元素的功能私有,这样它们就可以被脚本化和样式化,而不用担心与文档的其他部分发生冲突。
- HTML templates:
<template>和<slot>元素使您可以编写不在呈现页面中显示的标记模板。然后它们可以作为自定义元素结构的基础被多次重用。
结合微前端的概念,Web components 在某些方面做到了微前端:
- 技术栈无关:Web Components 是浏览器原生组件,那即是在任何框架中都可以使用。
- 独立开发:使用 Web Components 开发的应用无需与其他应用间产生任何关联。
- 应用间隔离:Shadow DOM 的特性,各个引入的微应用间可以达到相互隔离的效果。
综上所述,Web Components 是有能力以组件加载的方式将微应用整合在一起作为微前端的一种手段,但不幸的是,Web Components 是浏览器的新特性,兼容性还不够友好。
single-spa
亮点:
- 全异步编程,对于用户需要提供的 load,bootstrap,mount,unmount 均使用 promise 异步的形式处理,不管同步、异步都能支持
- 通过劫持路由,可以在每次路由变更时先判断是否需要切换应用,再交给子应用去响应路由
- 标准化每个应用的挂载和卸载函数,不耦合任何框架,只要子应用实现了对应接口即可接入系统中
不足:
- load 方法需要知道子项目的入口文件
- 把多个应用的运行时集成起来需要项目间自行处理内存泄漏,样式污染问题
- 没有提供父子数据通信的方式
qiankun
qiankun(基于 single-spa)是单实例场景下的微前端架构实践方案,因为这更贴合大部分中后台应用。它的定位是:导航路由+资源加载框架。
路由匹配机制:首先由主应用的路由匹配到某个子应用,框架先加载 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把获取资源和执行资源分离的点来提前加载所有子应用的资源。
总结:qiankun 在 single-spa 的基础上,提供了更多可适用于生产的开箱即用的功能,如:多种资源加载方式、样式隔离、沙箱机制、全局数据存储、资源预请求等,这大概是目前微前端最成熟的实现方案。
webpack5 module federation
webpack5 发布之初,其 module federation 特性就与微前端联系到一起。EMP 是由欢聚时代前端团队自研的单页微前端解决方案,它利用了 webpack5 module federation 新特性,实现了跨组件模块调用方式。
该方案更多特性如下:
- 基于 Webpack5 的新特性 Module Federation 实现,达到第三方依赖共享,减少不必要的代码引入的目的。
- 每个微应用独立部署运行,并通过 cdn 的方式引入主程序中,因此只需要部署一次,便可以提供给任何基于 Module Federation 的应用使用。并且此部分代码是远程引入,无需参与应用的打包。
- 动态更新微应用:EMP 是通过 cdn 加载微应用,因此每个微应用中的代码有变动时,无需重新打包发布新的整合应用便能加载到最新的微应用。
- 去中心化,每个微应用间都可以引入其他的微应用,无中心应用的概念。
- 跨技术栈组件式调用,提供了在主应用框架中可以调用其他框架组件的能力。
- 按需加载,开发者可以选择只加载微应用中需要的部分,而不是强制只能将整个应用全部加载。
- 应用间通信,每一个应用都可以进行状态共享,就像在使用 npm 模块进行开发一样便捷。
- 生成对应技术栈模板,它能像 create-react-app 一样,也能像 create-vue-app 一样,通过指令一键搭建好开发环境,减少开发者的负担。
EMP 微前端方案除了具备微前端的能力外,它还实现了跨框架组件调用的能力,这是现有框架所不具备的特性。
微前端方案选型
以上多种微前端通用解决方案,它们各自具备不同的特点,我们在技术选型时需要考虑的点包括但不限于:
- 平滑升级
- 技术栈无关(目前仅考虑 MVVM 框架,可选)
- 独立开发
- 独立运行(如果子应用依赖主应用才能启动,需要考虑无数据注入的场景)
- 独立部署
- 快速构建
- 单页应用体验
- 子应用切换速度
- 应用间通信方案
对比 iframe、web components、single-spa、qiankun、module federation 等方案,我们可以发现:
- iframe 是比较久远的微前端实现方案,它的硬隔离特性决定了它能解决一部分问题,但同时也会带来无法突破应用上下文共享,随之带来的开发体验、产品体验的问题。这个方案在我们之前的方案中得到验证,因此 iframe 不再考虑作为微前端方案。
- web components 天生隔离脚本与央视,该方案也在某些方面具备微前端的特性,但由于其是新特性,兼容性存在很大的问题,目前业内暂时没有出现用它解决微前端的方案,因此 web components 不再考虑作为微前端方案。
- single-spa 是现代微前端一种比较好的解决方案,它提供了路由分发机制,是一种比较偏底层的微前端解决方案,现在业内非常多的前端团队选择在 single-spa 的基础上,结合团队的项目定制化开发、拓展微前端能力,从而达到解决定制化场景的微前端方案。这个方案目前在业内被认可度较高,很适合在此基础上定制化开发,因此可以选为微前端解决方案。这个方案在公司内部一些团队也得到了实践,如目前的 moebius 前端团队是基于 single-sap 和自研 sdk 实现的路由分发+应用资源加载。
- qiankun 是基于 single-spa 的一种相对完善的开源的微前端解决方案,它在 single-spa 的基础上,提供能更多可适用于生产的功能,如:多种资源加载方式、样式隔离、沙箱机制、全局数据存储、资源预请求等。毫无疑问,这是是目前微前端最完善的可用于生产的实现方案。qinakun 方案诞生之初,在业内被广泛讨论,被认可程度也很高,具备较好的周边社区支持,在未来也会有较好的发展。
- emp 是一款基于 webpack5 module federation 的微前端解决方案,据其开发团队介绍,现在已经可用于生产,但由于模块联邦是一个新的概念,这仍然是一个比较年轻的实现方案,其稳定性还有待验证,因此该方案暂时不列为公司微前端选项。这在未来或许是更好的微前端解决方案,现在可以持续关注。
综上所述,具备安全、稳定、可行特性的微前端解决方案有:基于 single-spa 深度开发、qiankun 开箱即用。两种方案是业内关注度较高的解决方案,它们都是中心路由基座式设计思想,具备独立开发、独立构建、单页应用体验好、子应用切换速度快等微前端框架必备特点,其中 qiankun 方案更是支持了 快速构建、健全的应用间通讯、子应用运行时隔离、学习成本低等特性,可以让我们的微前端项目更稳定、更便捷的开发和部署,就目前来说 qiankun 方案仍然是走在微前端前面的那一个。如果希望定制化开发的可以选用 single-spa 再开发,如果希望开发基于减少学习成本可以选用 qiankun 快速应用到项目。
其他
除了微前端解决方案选型之外,我们可能还需要考虑一些微前端相关的周边问题,包括:
- 微前端周边脚手架:现在大多数开源的微前端框架都配备了脚手架工具,我们也可以选择内部定制。
- 微前端项目配套校验库:微前端对应于解耦后,多个子项目的管理也是个不得不考虑的问题,校验库此类配套设施,应根据项目具体定制。
- 微前端项目配套 git 工作流:微前端作为一种架构设计,会带来工作流的改变,如统一的版本发布、交付等。
- 微前端自研构建工具(可选):构建工具受技术栈的限制,我们可选的官方方案只有 vue-cli,这是一个不错的选择,但不一定是最适合项目的选择,在 vue-cli 基础上再开发或是完全自研一套构建方案,这是值得考虑的问题。
- ...
参考资料:
- 可能是你见过最完善的微前端解决方案 作者:蚂蚁团队 kuitos【qiankun 作者】
- 微前端的核心价值 作者:蚂蚁团队 kuitos
- 探索微前端的场景极限 作者:蚂蚁团队 kuitos
- qiankun 微前端原理与实践 作者:OPPO 互联网技术【qiankun 方案】
- 微前端在美团外卖的实践 作者:美团技术团队【基于 React 自研】
- 面向大型工作台的微前端解决方案 icestark 作者:阿里飞冰 ICE【自研方案,使用成本较高】
- 浅析微前端解决方案 icestark 原理 作者:阿里飞冰 ICE【自研方案,类 single-spa】
- 微前端在小米 CRM 系统的实践 作者:小米信息部技术团队【single-spa 方案】
- 网易严选企业级微前端解决方案与落地实践 作者:网易云团队【自研方案,类 single-spa】
- 每日优鲜供应链前端团队微前端改造 作者:每日优鲜大前端团队【single-spa】
- 适用于既有大型 MPA 项目的“微前端”方案 作者:有赞技术团队【自研方案,类 qiankun】
- 微前端的设计理念与实践初探 作者:王下邀月熊