微前端在民生 APaaS/PSET 平台的探索与实践

用户头像
亻尔可真木奉
关注
发布于: 2020 年 08 月 29 日
微前端在民生 APaaS/PSET 平台的探索与实践

前言



微前端,借鉴了后端微服务的理念,将它应用于前端,将一个单一的单体应用拆分成多个小型应用的集合,这些小型应用最终聚合成一个完整的庞大的应用。



民生应用云 APaaS 平台利用云原生技术,提供全生命周期的平台支撑,解决金融应用从传统架构向云原生架构转型的痛点。平台为应用开发人员提供了以 应用为中心的资源申请,应用建模,DevOps,环境管理等功能。



民生软件工程支撑平台(PSET)提供从软件需求到交付运营的全生命周期支撑能力,能有效减少研发成本、量化研发过程、提高交付质量、缩短交付周期,协助项目组高质量高效的交付软件,目前民生 PSET 平台已为全行提供 DevOps 服务。



在民生 APaaS 平台初期搭建时,考虑到其 DevOps 体系与民生 PSET 平台有许多交集,且 PSET 平台已较为成熟且符合目前行内项目组敏捷开发流程,所以部分功能需要与民生 PSET 平台进行集成开发,这就需要用户在两个平台(甚至更多平台)之间频繁切换,平台间的跳转会带来几个副作用:



  • 会出现白屏, 使用户在使用时有跳出感,体验不佳。

  • 用户在各个平台间跳转时会产生困惑,不知道某个功能具体是在哪里实现,出现问题时难以定位溯源。

  • 由于帐号体系及配置隔离,平台之间难以通信,一些基础配置信息不能同步,需要多次填写。



我们也尝试将 PSET 平台现有业务组件迁移至 APaaS 平台内进行复用,但由于两个平台框架存在版本及兼容性等问题,组件之间并不能进行无成本的复用,大多都需要进行改造,而且组件的复用也造成了前端工程过于复杂和庞大,这样并不容易进行维护,而且 PSET 平台的相关改动还要手动维护至 APaaS 平台,对于开发者也并不友好。



于是我们进行了一些调研和分析,尝试使用微前端的方式将两个平台进行集成。



民生应用云 APaaS 平台前端集成方式的探索



需求分析



结合 APaaS 平台和 PSET 平台现状,我们对两者集成方式进行了深度分析,首先明确了集成过程中需要解决的问题,包含以下几点:



  1. 用户体验。两平台之间需要频繁的交互和跳转,我们希望在平台之间切换时做到用户无感知。



  1. 开发体验。我们希望两个平台前端工程能够独立开发和维护,两个平台的功能开发,测试和部署互不影响。



  1. 部署方式。每个项目应该能够同时支持独立发布和集成式发布两种部署方式。同时,在集成式方式发布时,可单独部署任何一个项目而不影响其他项目。



  1. 技术栈。现阶段,前端技术百花齐放,优秀框架层出不穷,我们希望能够集成尽可能多的框架,使用我们既可以确保现有项目的集成,还可以确保在若干年后同样能使用时下最新最流行的技术栈。



5,改造成本。尽可能少的修改现有平台内部代码,以降低日后其他平台的接入成本。



方案探索



经过对以上需求的分析,我们对时下流行的前端集成方案做了调研,同时与民生 Apollo 前端团队探讨交流,初步提出了以下几种解决方案:



  1. iframe嵌入式集成

iframe 嵌入作为一个普通却有效的方法,在技术层面上,完全可以实现当前的功能需求,而且使两个平台可以完全独立管理和维护。但是,iframe 嵌入显示效果并不理想,而且需要平台提供不包含独立导航栏的页面,且路由管理困难,每次刷新页面时,都会跳回最初的 iframe 地址。



  1. 路由分发式集成

通过 HTTP 服务器的反向代理来实现将路由不同的业务分发到不同的、独立的前端应用上。这种方案更像是多个前端服务的集合,我们只是把他们拼凑到一起,看起来像是一个整体,在实际使用是仍然是从 A 应用跳转到 B 应用。



  1. 通用中心路由基座式集成

提供一个基座工程,在基座工程里并不包含任何子工程的逻辑和业务场景,只是提供了各个子工程的注册和挂载逻辑,基座工程会根据当前路由,自动挂载与之匹配的子工程。这种方式使子工程可以使用不同的技术栈,且子工程之间相互独立,实现独立开发,测试,部署。



考虑到民生 APaaS 平台后续会更多的集成其他现有平台及第三方平台,经过对以上三种解决方案的调研、对比与探讨,我们认为 iframe 嵌入需要维护大量 iframe 路由关系,且集成效果并不理想,会产生较多的样式等问题,路由分发式对于前端来说并没有解决根本问题,其无法处理页面跳转时的白屏问题,且平台之间通信困难,而且集成后整体性不强。通用中心路由基座式集成方式与前两种比较,既不用维护像iframe嵌套时产生的大量路由关系,又可以关联各个子工程,使子工程之间可以实现较为优雅的通信,而且在子工程切换时可以较为平滑,最终我们选择了通用中心路由基座式集成方案。



整体方案架构如下图所示:



基座工程



我们选择使用 Single-SPA,



它具有以下优势:



1,在同一页面上使用多个前端框架 而不用刷新页面。



2,独立部署每一个单页面应用



3,新功能使用新框架,旧的单页应用不用重写可以共存



4,改善初始加载时间,迟加载代码



同时,民生 Apollo 前端团队针对 Single-SPA,提供了 apollo-mfe-cli 工具, 提供初始化的基座工程,这极大地简化了基座工程的配置流程,减少了配置项。



子工程



我们使用 single-spa-apollo 作为子工程的适配器, single-spa-apollo 针对民生 Apollo 框架项目做了深度的适配,使用民生 Apollo 前端框架的项目在进行微小改动后即可顺利的转换集成为子工程。



下面以民生 PSET 平台前端改造为例,说一下子工程的改造过程:



  1. 基座工程注册子工程。



我们在基座工程内注册 pset 子工程,我们需要配置子工程的应用名和唯一的路由,使路由访问/pset/*时,基座工程可以正确的加载应用名为 pset 的子工程。entryFiles 为加载子应用后,子应用初始化时需要加载的文件。具体配置为



loadingPromises.push(
registerApp({
name: 'apaas', // 唯一的应用名
route: `${location.pathname}apaas`, // 唯一的路由
entryFiles: [ 'shim', 'default-common','mfeEntry'], // 构建应用后的入口文件数组
activityFunc: hashRouteActivityFunc('/apaas'), // 激活应用的路由规则
appStyleId: 'mfe-style' // 样式表容器 id
})
);



  1. 子工程增加入口文件



为 PSET 子工程创建一个新的微前端打包入口文件。民生 PSET 平台作为一个独立的项目,有完整的打包流程及入口文件,而作为微前端子工程,需要一个可以供给基座 工程挂载的文件,这就需要一个不同的入口文件。

这个入口文件也并不复杂, 使用 single-spa-apollo,只需要将原来 app.start()所生成的文件,挂载到reactComponent 内即可,其他的配置,single-spa-apollo 已经帮我们做了相应的处理。(以下代码为新增入口文件与原入口文件对比)



// /src/mfeEntry.js
// 修改前
app.start();
// 修改后
const rootComp = app.start() // 带有 Provider 和 router
const cachedStyles = []
// 缓存样式表
const cacheStyle = function() {
const mfeStyle = document.getElementById('mfe-style')
const links = mfeStyle.getElementsByTagName('link')
for (let i = 0, len = links.length; i < len; i++) {
cachedStyles.push(links[i])
}
mfeStyle.innerHTML = '' // 清除微应用样式
}
// 恢复样式表
const restoreStyle = function() {
const mfeStyle = document.getElementById('mfe-style')
cachedStyles.forEach(link => {
mfeStyle.appendChild(link)
})
}
const reactLifecycles = singleSpaReact({
React,
ReactDOM,
rootComponent: rootComp,
domElementGetter
})
export function bootstrap(props) {
// 固定接口请求路径
return reactLifecycles.bootstrap(props)
}
export function mount(props) {
restoreStyle()
return reactLifecycles.mount(props)
}
export function unmount(props) {
app = null
cacheStyle()
return reactLifecycles.unmount(props)
}
function domElementGetter() {
let el = document.getElementById('react-content1')
if (!el) {
el = document.createElement('div')
el.id = 'react-content1'
document.getElementById('mfe-main').appendChild(el)
}
return el
}



  1. css隔离



在 single-spa-apollo 内提供了组件加载的生命周期,我们在组件挂载时加入样式表,在组件卸载时缓存并删除样式表,动态加载 css 样式,使页面效率更高, 同时,我们为每个子工程样式表生成命名空间,来确保子工程样式不会污染全局样式。



  1. 添加打包配置



在构建民生 PSET 平台子工程时,需要使用额外的 webpack 配置,这包含项目在基座工程内注册时的应用名,入口文件,路由规则等配置,这些配置将在构建微前端子组件时注入 webpack 配置中。



if (process.env.isInMFE) {
const APP_NAME = 'pset' // app name
const APP_ROUTE_PATH = `${mfeRouterContext}` // app route path
// const OUTPUT_PATH = './dist' // build path
const APP_ENTRY_NAME = 'mfeEntry' // app entry name
const STYLE_CON_SELECTOR = '#mfe-style' // style containter's dom selector
if (webpackConfig.module.rules) {
webpackConfig.module.rules.unshift({
// SystemJS compatible with webpack
parser: { system: false }
})
}
webpackConfig.entry = {
[APP_ENTRY_NAME]: `./src/${APP_ENTRY_NAME}.js` // app entry
}
webpackConfig.output = {
...webpackConfig.output,
path: path.resolve(__dirname, OUTPUT_PATH),
publicPath: `${APP_ROUTE_PATH}/`, // micro front-end static resource path
libraryTarget: 'window',
library: `${APP_NAME}App` // export library name
}
}



5.增加构建命令,增加持续集成单元,独立部署



为了使 PSET 平台可以同时支持单独部署和微前端部署,我们增加了微前端的打包命令,在 package.json 中,通过改变打包时的环境变量,输出不同的bundle。



"build": "cross-env buildMFE=false apollo-build build --analyze",
"start": "cross-env buildMFE=false islocalhost=true apollo-build server",
"start:mfe": "cross-env buildMFE=true PORT=9006 BROWSER=none CLEAR_CONSOLE=none isInMFE=trueislocalhost=true apollo-build server",
"build:mfe": "cross-env buildMFE=true isInMFE=true apollo-build build",



项目在服务器部署时整体结构如下:



app
├── portal //基座工程目录
├── apaas // APaaS 子工程目录
└── pset // PSET 子工程目录



每次民生 PSET 平台更新时,只需要将项目打包后的 dist 目录内文件更新至服务器 pset 目录内,portal 和 apaas 项目并不需要任何操作,当我们再次访问基座工程时,就能正确的挂载更新后的 PSET 子工程。而打包部署等操作可在民生 PSET 平台直接配置流水线,实现自动化打包部署。



民生 PSET 平台微前端改造总结



优势:



  1. Single-SPA 对于其子工程的技术栈不敏感,这保证了老项目集成及新项目扩展的可行性。使用 Single-SPA,我们在集成老项目的同时,还可以确保在若干年后同样能使用时下最新最流行的技术栈。这可以是我们的项目保持旺盛的生命力。



  1. Single-SPA 将项目拆分为多个子工程,使每个子工程可以由专门的团队单独维护,这降低了维护成本及沟通成本,使每个团队可以将注意力集中在自身子工程的迭代中。



  1. Single-SPA 使每个子工程可单独维护自己的代码仓库,这降低了子工程之间的耦合性,使每个子工程可以单独打包,测试,部署,提高了开发效率。



  1. Single-SPA 通过基座工程,将各个子工程通过路由挂载,在子工程之间切换时,保持了整体的样式,没有跳出感,同时子工程对样式的缓存,使子工程之间切换时响应更加迅速,页面展现更加流畅。



不足:



  1. 由于目前 Single-SPA 内只是对入口文件做了修改,并没有改变原有项目内部逻辑,而集成后,原有项目与其他项目之间的跳转关系等发生了改变,这就需要项目内针对集成的子工程的跳转逻辑进行判断及改造,前期有一定的工作量。



  1. 目前基座工程内未集成统一身份认证功能,各子工程的登录状态还由各自维护,造成了登录状态的混乱,待所有项目接入统一身份认证平台后,该问题才会得到解决



3,子工程之间通信还是依赖 URL 参数,基座工程并没有提供查询子工程状



态的功能,基座工程应该提供统一的方法,可以使子工程向基座工程暴露自身的



某些状态,数据信息,供其他子工程调用。



结语



以上就是我们团队在民生 APaaS 平台开发初期进行微前端改造与实践方面的一些经验。我们对于微前端探索之路还很漫长,相信在微前端这样的架构基础上,各平台的前端集成及团队管理效率会有所提升。我们也会加深加强对微前端的学习及思考,相信随着不断的探索和改进,微前端一定能够更加符合项目需求,为项目提供更好的服务,这也是我们前端软件工程的探索方向,希望这些能够对大家有所帮助。



随着平台的逐渐完善,我们也发现了实际使用时的痛点,我将在后续的文章中总结一下在平台使用微前端一年后的今天,我们遇到的问题及解决方案,包括业务实现及工程管理等方面的经验教训。



发布于: 2020 年 08 月 29 日 阅读数: 471
用户头像

亻尔可真木奉

关注

hello,wor烫烫烫 2020.04.02 加入

还未添加个人简介

评论

发布
暂无评论
微前端在民生 APaaS/PSET 平台的探索与实践