写点什么

微前端在民生 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 日阅读数: 564
用户头像

hello,wor烫烫烫 2020.04.02 加入

还未添加个人简介

评论

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