写点什么

微前端框架 single-spa 子应用加载解析

  • 2023-03-28
    北京
  • 本文字数:3910 字

    阅读完需:约 13 分钟

微前端框架single-spa子应用加载解析

作者:京东物流 宁冲

1 前言

什么是微前端?


微前端是指存在于浏览器中的微服务。


本文主要通过对微前端框架 single-spa 的基座应用加载子应用的 single-spa-vue 函数库进行分析,通过代码维度分析让大家了解在 single-spa 加载子应用的时候都做了哪些事情。如何通过优化 single-spa-vue 函数库保持子应用的状态。


由于是在代码维度进行分析,要求读者对 single-spa 有一定的了解,阅读效果会更好。

2 single-spa 加载子应用的过程

基座应用中配置的加载子应用配置,在配置子应用对象的 app 方法中会把子应用 chunk-vendors.js、app.js 插入到页面,并且执行 chunk-vendors.js、app.js 两个子应用依赖的 js。



app 方法执行结束以后,子应用依赖的 js 插入页面以后的 dom 结构。



由上面 app 方法的执行过程,我们可以看出基座应用加载子应用,和 vue 框架打包后加载 js 资源的方式类似。那么为什么只需要加载子应用的 app.js 和 chunk-vendors.js 就可以把子应用渲染到页面上面。下面我们再来看看子应用的入口文件 main.js 方法中是如何进行配置的。

3 子应用的 main.js 中的配置

在子应用 main.js 中我们可以看到,把常规的配置 1 替换为配置 2 的格式。



在配置 2 中我们会把当前 vue 的配置传给 single-spa-vue 函数库中。包括 el、render 方法、Vue 对象。通过 singleSpaVue 的包装,返回 single-spa 生命周期方法 bootstrap、mount、unmount。


  • bootstrap:引导函数,应用内容首次挂载到页面前调用,只会执行一次。

  • mount:挂载函数,子应用每次被挂载的时候都会执行。

  • unmount:卸载函数,子应用每次被卸载的时候都会执行。


也就是说基座应用在加载子应用的时候就是通过 single-spa-vue 函数的处理,生成了 single-spa 的各个生命周期,给基座应用在加载子应用的时候调用。


下面我们来看看 single-spa-vue 究竟是如何生成各种生命周期函数的。

4 single-spa-vue 源码结构

single-spa-vue 函数库可以帮助我们来生成加载子应用的生命周期。你可以通过下面链接:single-spa-vue GIT 地址,下载 single-spa-vue 函数库的源码。


single-spa-vue 的源码非常简单,只有几个生成 single-spa 生命周期的函数。



  • single-spa-vue 方法:single-spa-vue 函数库对外提供服务的唯一一个方法,用来接收配置项,并且返回一个包含各个 single-spa 生命周期的对象。

  • single-spa-vue 中的其他方法 bootstrap、mount、update、unmount 是用来生成 single-spa 对应生命周期函数的方法。


下面我们详细介绍一下这几个方法的作用和实现。

5 single-spa-vue 源码解析

single-spa-vue 中提供的 singleSpaVue 方法会接收 userOpts 配置信息,配置信息会和默认的配置项 defaultOpts 合并以后再进行进行后续处理。

5.1 配置项 defaultOpts

对于默认的配置项会有下面这几项,这里只介绍一下必填项,appOptions、Vue/createApp。其中的 template 在并没有在 single-spa-vue 中被用到,但是在 single-spa-html 中有使用到。


  1. // 默认配置项列表

  2. const defaultOpts = {

  3. // required opts

  4. appOptions: null,

  5. template: null,

  6. // sometimes require opts

  7. Vue: null,

  8. createApp: null,

  9. handleInstance: null

  10. }


1)appOptions 配置项介绍


  • appOptions:应用的配置项,会在初始化 Vue 实例的时候作为参数传给 Vue 方法,下面具体介绍一下 appOptions 中的配置项。

  • el:子应用需要挂载的基座 dom,即 vue 需要挂载的 dom。

  • render/template:vue 的 render/template 配置项。

  • data:初始化的参数对象,会在执行挂载函数 mount 的时候直接挂载到 vue 实例上。


2)Vue/createApp 配置项介绍


Vue/createApp 配置项是用来生成 vue 实例的,single-spa-vue 函数库可以通过传入的 Vue 对象在 mount 方法中来生成 vue 实例。也可以传入 createApp 方法,由子应用在 createApp 方法中返回 vue 实例。


下面我们会在生命周期生成方法中看到这些配置项的作用

5.2 入口方法:singleSpaVue

singleSpaVue 方法会对入参先进行下列有效性校验,具体校验的内容有一下四项。


  1. 配置项 userOpts 是否为对象。

  2. 用来创建 vue 实例的配置 Vue/createApp 是否存在。

  3. 用来生成 vue 实例的配置项 appOptions 是否存在。

  4. vue 实例挂载的 dom 是否正确 appOptions.el 有效性校验。



有效性校验通过以后,会调用 bootstrap、mount、unmount、update 方法用来分别生成 single-spa 加载子应用的生命周期函数。


下面详细介绍一下各个生命周期函数是如何生成的。

5.3 引导函数:bootstrap

引导函数 bootstrap,当应用内容首次挂载到页面前调用。



bootstrap 函数,会先判断一下是否在配置项中存在 loadRootComponent。配置项 loadRootComponent 笔者理解是可以用来加载当前子应用依赖的父级应用或者其他的依赖资源。


比如 A 页面依赖 B 组件,那在加载 A 页面的时候可以 loadRootComponent 方法中把 B 组件的静态资源加载下来,并且渲染到页面上,A 页面就可以直接使用 B 组件中提供的一些资源或者 dom。


如果未配置 loadRootComponent,则直接返回,不做任何处理。

5.4 子应用挂载生命周期:mount

在子应用每次被挂载到页面上的时候,single-spa 会执行 mount 生命周期函数,single-spa-vue 在 mount 函数中用来初始化子应用的 vue 实例,并且把实例挂载到页面上。


mount 函数会接收三个参数,分别是 singleSpaVue 传入的参数 opts,single-spa-vue 当前全局挂载的子应用实例列表 mountedInstances,在基座应用中注册的当前子应用的 single-spa 实例 props


props 入参接收的数据,这里只用到了 props 的 name 属性,用来标识当前挂载的子应用。



在 mount 方法中主要做了以下几件事


1)格式化应用配置项 appOptions


mount 方法中会通过 resolveAppOptions 方法来格式化应用的配置项 appOptions。


在 resolveAppOptions 方法来中如果 appOptions 是一个方法的话,则执行方法,并且传递基座应用通过 customProps 参数传入的参数 props。


子应用可以在 appOptions 方法中获取父应用传递的参数、根据父应用的状态做不同的处理等。



2)初始化子应用需要挂载的 DOM 对象。


初始化 dom 的配置和方法比较多,会从很多个配置项中来获取 el,代码比较简单,这里就不赘述了。只列出来取值的优先级供大家参考。


appOptions.el > props.domElement > props.name



dom 节点被格式化完成以后,下面就开始把页面挂载到 dom 上面。


3)创建 vue 示例,并且挂载到页面上


在挂载页面之前会有一个配置项:replaceMode,replaceMode 配置项是用来标识是否需要替换 el 节点下的内容。默认 replaceMode 为 false 是会在 el 节点下面新建一个 class 为 single-spa-container 的空白 div 来保存子应用。


以 demo 中的代码为例:



默认 replaceMode 为 false 的情况下,基座中的的内容和 vue1 中的内容会并存。如下图:



如果把 replaceMode 改为 true 的情况下,会发现基座中的内容会被 vue1 中的内容覆盖掉。


如下图:



设置挂载方式以后,就开始把子应用真正挂载到 dom 上面。


这里支持两种挂载方式,一种是使用 vue3 的 createApp 方式挂载,一种是使用 vue2 的 new Vue 方式进行挂载。这里的挂载方式其实和我们在使用 vue 挂载到 dom 方式的配置是一样的。



通过上面的步骤,此时子应用就被挂载到了 html 的页面中的 dom 上了。

5.5 更新生命周期函数:update

当调用 parcel.update()会触发 update 方法,笔者由于并没有用的高级特性 parcel,在此处不再做过多介绍。

5.6 子应用卸载生命周期:unmount

当页面 url 发生变化的时候,离开子应用页面路由的时候会触发 unmount 方法。


在 unmount 方法中主要做的事情有:


  1. 卸载 vue 应用

  2. 删除 vue 实例

  3. 同时清空页面 dom。



执行 unmount 操作以后子应用的数据和 dom 就会被清除干净。

5.7 小结

以上就是 single-spa 官方提供的 single-spa-vue 函数库来挂载子应用的全部流程。通过上面的分析,我们发现 single-spa-vue 函数库会通过调用 singleSpaVue 方法返回一个包含 bootstrap、mount、update、unmount 四个方法的对象。在基座应用加载子应用的生命周期会执行对应的方法,通过执行方法把子应用挂载到基座应用或者从基座应用销毁子应用。


通过 single-spa-vue 中加载子应用的过程,single-spa 把各个子应用组装成一个复杂的应用,在用户无感的情况下对各个子应用进行分别治理。


single-spa 在加载子应用的时候,除了 vue 的版本之外,还有其他的版本,但是加载的思路类似,都是在函数库中来把子应用加载到 html 中,此处不再介绍其他类型的加载。


single-spa 其他加载子应用方式:single-spa-react、single-spa-html

6 子应用状态保持

在实际开发的过程中笔者遇到有些页面需要使用 vue 中的 keep-alive 来保留状态,在页面切换的过程中虽然页面被销毁但是页面状态需要在保留,但是我们在分析 single-spa-vue 的 unmount 方法实现的时候发现,如果页面切换,子应用和 vue 实例被销毁,此时子应用中的 keep-alive 是没有生效的。


如下图:



当在 input 中输入内容以后,传统的 spa 项目可以通过 keep-alive 来记录状态,当页面路由切换以后,再切回来,仍然保持状态。但是如果直接使用 single-spa-vue,当子应用触发 unmount 的时候 input 中的输入内容 1111 会被清空。针对这种情况,笔者对 single-spa-vue 类库进行了一些改造。


改造内容如下:

6.1 子应用由销毁改为隐藏

在 single-spa-vue 配置项中增加了一个配置项,根据配置项中是否存在 isKeepAlive 配置项,来判断把当前的 dom 隐藏掉,还是删除。这样当子应用 unmount 方法触发的时候,子应用并未被删除,而是仍然保留。



同时笔者对 vue 的 route 配置也进行了一些优化,当页面不存在的时候,此时会把子应用中的一个空组件挂载到 dom 上面,避免虽然页面被隐藏掉,但是 dom 仍然在 html 中,导致页面 dom 过多。


6.2 子应用由新建改为显示

在 single-spa-vue 配置项中调用 mount 方法挂载子应用的时候,会判断当前子应用是否存在,如果子应用存在则直接把子页面显示出来,由于子应用并未被销毁,此时子应用中的 keep-alive 就会一直生效,并且保存页面的状态。



通过对 single-spa-vue 类库的 mount、unmount 方法的优化,用户在使用页面的时候真正和使用 vue 那样流畅,并且可以保持页面状态,提升用户的体验。

发布于: 20 分钟前阅读数: 5
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
微前端框架single-spa子应用加载解析_生命周期_京东科技开发者_InfoQ写作社区