qiankun微服务--介绍及其搭建

102 阅读7分钟

一、微前端概述

1.基本原理

在介绍qiankun之前,我们要知道,它是基于single-spa的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生成可用的微前端架构系统。qiankun在它的基础上进行了封装和增强,使其更加易用。

微前端架构具备以下几个核心价值:

  • 技术栈无关

    主框架不限制接入应用的技术栈,微营销具备完全自主权

  • 独立开发、独立部署

    微应用仓库独立,前后端可独立开发,部署完成后主框架自动完成同步更新

  • 增量升级

    在面对各种复杂场景时,我们通常很难对一个已经存在的系统做全量的技术栈升级或重构,而微前端是一种非常好的事实渐进式重构的手段和策略

  • 独立运行时

    每个微应用之间状态隔离,运行时状态不共享

微前端的概念借鉴自后端的微服务,主要是为了解决大型工程在变更、维护、扩展等方面的困难而提出的。

目前主流的微前端方案包括以下几个:

  1. iframe
  2. 基座模式,主要基于路由分发,qiankunsingle-spa 就是基于这种模式
  3. 组合式集成,即单独构建组件,按需加载,类似npm包的形式
  4. EMP,主要基于Webpack Moudle Federation
  5. Web Components

iframe:是传统的微前端解决方案,基于iframe标签实现,技术难度低,隔离性和兼容性很好,但是性能和使用体验比较差,多用于集成第三方系统。

基座模式:主要基于路由分发,即有一个基座应用来监听路由,并按照路由规则来加载不同的应用,以实现应用间解耦。

EMP:基于 Webpack5 Module Federation,一种去中心化的微前端实现方案,它不仅能很好地隔离应用,还可以轻松实现应用间的资源共享和通信。

Web Compoents:是官方提出的组件化方案,它通过对组件进行更高程度的封装,来实现微前端,但是目前兼容性不够好,尚未普及。

qiankun的主要思路是将一个大型应用拆分成若干个更小、更简单、可以独立开发、测试和部署的子应用。然后由一个基座应用根据路由进行用过切换。

基座应用:即父应用,主要负责集成所有的子应用,提供一个入口能够访问你所需要的子应用的展示。

子应用:对应的业务的项目或者是对应的业务的模块。子应用的打包需要输出成模块的形式,父应用通过这个来加载模块。

下面我们对qiankun所基于的基座模式进行介绍。

我们用以前的前端组件的概念做类比,我们可以把每个被拆分的子应用看做是一个应用级组件(子应用),每个应用级组件专门实现某个特定的业务功能(如资产管理、计费缴费、园区运行、营销管理等),这里实际上谈到了微前端的拆分原则:即以业务功能为基本单元。经过拆分后,整个心态的结构也发生了变化:

1697450816397.png

如上图所示,左边是传统大型单页应用的前端架构,所有的模块都在一个应用中,由应用本身的负责路由管理,属于应用分发路由的方式。而右边是基座模式,各个子应用互不相关,单独运行在不同的服务上,由基座应用根据路由选择加载哪个应用到页面内,是路由分发应用的方式。这种方式使得各个模块的耦合度大大降低,而微前端需要解决的主要问题就是如何拆分和组织这些子应用。

为了让这些拆分的子应用在一个单页面内协同工作,我们需要一个“管理者”应用,也就是基座应用,也交主应用。基座应用一般是用户最终访问的应用,它会根据定义的规则,将不同的应用加载到页面内给用户使用(每个子应用也具备单独访问的能力)。

为了配合基座应用,自应用必须经过一些改造,向外暴露出相应的生命周期钩子,以便基座应用加载和卸载。

1697451268457.png

在传统的React项目中,各个组件必须基于react编写。但是在微前端架构中,各个子应用可以基于不同的技术框架,对应大型项目来说,这样能很好维护、重构、技术升级。在微前端架构中,各个子应用将一些特定的业务功能封装在一个业务黑箱中,支队外暴露少量生命周期方法;基座应用根据路由地址变化,动态加载对应的业务黑箱,并将其渲染到指定的站位DOM元素上。

首先我们项目采用的协议入口是single-spa,只要是实现了single-spa的入口协议规范,它就是可加载的应用。single-spa的规范要求应用入口必须暴露以下三个生命周期钩子函数,且必须返回Promise,以保证single-spa可以注册回调函数:bootstrapmountunmount

1697525792022.png bootstrap用于应用引导,基座应用会在子应用挂载前调用它。例如,某个子应用要挂载到基座应用内id为app的节点上,但是基座应用中当前没有id为app的节点,我们就可以在子应用的bootstrap钩子内手动创建这样一个节点并插入到基座应用中,子应用就可以正常挂载了。所以它的作用就是做一些挂载前的准备工作。

mount用于应用挂载,就是一般应用于渲染的逻辑。通常会把它封装到一个函数中,在mount钩子函数中调用。

unmount用于卸载,可以在这里调用实例的destroy方法手动卸载应用,或清楚某些内容占用等。

除此之外,还支持非必须得loadunloadupdate等,分别用于加载、卸载、更新应用。

主菜

我们项目所用的qiankun是基于基座模式的,所以必然有一个基座应用(即主应用),来管理各个子应用的加载和卸载。

如上图所示,上侧是主menu菜单区域或者左侧menu菜单,根据用户点击不同的菜单会在中间区域加载不同的内容。一般微服务的子应用不宜拆分太细,大部分都是以业务进行大致的拆分。我们将menu菜单放在基座应用中实现,然后将每个菜单用一个微应用来实现。这样,当我们点击menu菜单时,就会切换不同应用。

2.主应用搭载

3.子应用搭载

子项目的话,按照qiankun文档需要暴露出三个函数,对应子应用的生命周期bootstrapmountunmount,首先修改入口文件,在入口文件中添加这几个暴露出的函数:

 /**
  * bootstrap 只会在微应用初始化的时候调用一次,下次微应用重新进入时会直接调用 mount 钩子,不会再重复触发 bootstrap。
  * 通常我们可以在这里做一些全局变量的初始化,比如不会在 unmount 阶段被销毁的应用级别的缓存等。
  */
 export async function bootstrap() {
     console.log('react app bootstraped');
 }
 ​
 /**
  * 应用每次进入都会调用 mount 方法,通常我们在这里触发应用的渲染方法
  */
 export async function mount(props) {
     console.log('[react16] props from main framework', props);
     window.qiankunProps = props;
     render(props);
 }
 ​
 /**
  * 应用每次 切出/卸载 会调用的方法,通常在这里我们会卸载微应用的应用实例
  */
 export async function unmount(props) {
     ReactDOM.unmountComponentAtNode(
         props.container ? props.container.querySelector('#zlRoot') : document.getElementById('zlRoot'),
     );
 }

为了能够区分子应用的应用环境在聚合中还是单独的访问,新增了一个静态的配置文件public-path.js文件:

1697534540837.png 修改入口文件,根据不同的环境进行不同的加载:

1697534974183.png 在打包配置中配置:

1697535491143.png umd格式的应用会向外暴露指定的生命周期钩子函数,便于single-spa解析,在webpack4.X中需要配置jsonpFunction配置,webpack打包之后保存在window中的key,只需保证各个子应用不一致即可。其余配置是解决一些加载路径和解析文件问题。