前端于我
微前端 / Vue / qiankun

微前端的应用

微前端这个概念近两年才在国内火起来,但是之前只是简单的了解,所以对其看法有点偏颇。但是在入职目前的公司后,却深刻体会到微前端并不是一种技术概念,而是一种实在可行的解决方案。

它解决的并不是单纯的拆分巨石项目,而是对于多系统的整合,让其既达到功能的统一,又达到代码层面的统一,也即是能够像组件一样,自由组合每个系统,进而达到对于操作体验与编码体验的优化的效果。

背景

首先说一下背景吧,我们团队主要负责公司内部采购线部分的系统,而采购线又分好几个步骤,比如采购,质检,耗材管理等等,所以这些步骤分由不同的团队各自负责跟进,而因部门职责不同,系统又被拆分成独立系统,单独开发单独部署。

这既是考虑到系统独立所带来的稳定性,使其不会因为bug导致整个流程的崩溃,也是因为实际上单独一个部门平常也仅仅只需要一个系统就可以进行日常工作。比如质检人员,工作所涉及的内容仅使用质检系统便可以解决。

但是凡事皆有例外,也有一些例如主管、采购跟单之类的人员或者需要跨系统流程处理工作的情况。针对这种情况,我们之前的方案其实是非常简单粗暴的。

原有架构

原本各系统单独部署,通过统一的目录配置,展示所有系统的目录。点击本系统内目录是系统内部路由跳转,点击其他系统目录则直接跳到其他系统。此外还有一个独立的登陆系统供各系统统一登陆。

大概架构示意图如下:

原系统架构

这样的架构设计会带来如下的一些问题:

  1. 全局状态无法共享,各系统各自维护一套全局状态
  2. 系统跳转后原系统操作状态无法保持
  3. 各系统UI细节难以统一,且当需要修改统一部分的UI(如页头和目录)需要单独修改每个系统
  4. 切换系统会白屏

那么微前端解决的就是上述的问题。国内目前比较成熟的微前端结构应该就是阿里的qiankun,说实话接入乾坤的改造成本其实非常的低,大家上乾坤的官网看看它的接入教程就会发现,你除了准备一个主应用(基本上不会有太过复杂的东西,也无技术栈要求),就只需要在子应用稍微的修改一小段代码。

至于如何改造接入,这里就不赘述了,相信看官方文档会更加明了。由于我们团队内部的应用基本都是从同一套模版拷贝过来的,所以统一按照官网教程改就可以了。

如何保存各子应用的状态

这里着重需要了解一下的是如何保存各子应用的操作状态以及数据。在乾坤的文档中,子应用切换走后,这个子应用是要被注销清除的,然后再次激活则是重新初始化,重新渲染页面。它这么做的原因是因为避免多系统共存时导致的状态影响和避免多系统共存导致内存占用太高进而崩溃或泄漏。

这就跟我们预期要优化的功能点冲突了,子应用间切换时我们需要保存前一个子应用的状态。因为多系统间是存在上下游关系的,所以需要查询一条数据的状态流转时基本上都要从各系统上下游查询,此时如果切换应用时没有保存状态,那么当操作完一条数据,就不能接着进行吓一条数据的开发与查询,就还得从头搜索、翻页一顿操作。

言归正传,不解释那么多了,我们要知道的只有一点,就是当子应用切换时,不能清除原子应用的状态,我们要留住它。

实现起来也很简单,第一,将原本qiankun生命周期中unmount里面的清楚当前应用的代码去掉;第二,在mount生命周期中判断一下, 不要重复实例化,使用同一个实例重复插入到制定节点就可以了。如:

let inited = false
export async function mount(props) {
  if (!inited) {
    render(props)
    inited = true
  } else {
    props.container.removeChild(props.container.querySelector('#app'))
    props.container.appendChild(instance.$el)
  }
}

接下来最重要的一点是,你需要保持你页面的keep-aliverouter-view一直都在,也就是说即使当前路由不是本系统的,你也必须要渲染一个空的router-view,这样你才能使得keep-alive一直生效,一直保存你页面的状态。

实现起来也很简单,我们只要额外指定一个全路由匹配的路由,且让它渲染layout组件即可:

如:

{ path: '*', component: Layout }

这样,我们就简单的实现了子应用切换时保持应用状态不被注销的功能。

总结

其实对于微前端有很多比较简单粗暴的技术方案,比如一直被提及的iframe,或者是我们之前在用的系统间直接跳转。任何技术在解决问题的同时都或多或少的带来了一些新的问题,而我们要做的是通过衡量得失去判断哪个解决方案更合适我们去执行。

qiankun自然不可避免的也引入了一些问题:

  1. 多系统共存,尤其是多系统实例共存时内存爆栈和溢出风险也会成倍加大

  2. 各子系统需要格外避免全局污染(css, js)

  3. 加大了系统复杂度,包括子系统接入规范, 父子/子子系统通信,子系统单独访问等。

但是总的来说,接入成本很小,网站体验优化程度大。而最重要的是,我们不能被日常开发给局限住,而是永远都要打破局限,给自己更高的挑战。

后续考虑了解一下被qiankun隐藏在身后的single-spa,深入的了解一下它的设计理念与实现方式。

技术就如逆水行舟,不进则退,共勉吧。

发表于: 2021-11-07