「微前端实践」umi不同版本接入qiankun,踩坑到解决的过程

2,243 阅读9分钟

不瞒大家说,一分钟前,我还在准备软考,结果,收到了软考延期的通知。最近很忙,一边负责项目方面的工作,一边备考,前几天就想要写一写最近碰到的问题,因为备考就无限期的延期了。好在当下,软考取消了,可以有时间来为前几天的工作做个总结了。

一、聊聊项目现状

老生常谈,在讲技术实现前,先聊一聊项目现状。项目开始于3年前,采用前端技术栈为umi2,umi是什么,可以参考官网umi,顺便一提,目前umi已经升级到4了。那,这次项目要做些什么呢?简单来说,有两个需求需要实现,第一,增加新的功能模块,模块需要采用vue3的技术栈。第二,将增加的模块要融合到当前项目中,客户方要无感知的使用。也许有人会问,为什么采用vue3,而不是在老项目上迭代优化,因为公司今年在做技术规范,要求前端使用新技术实践。

需求我想,我已经介绍清楚了,很典型的微前端场景,新增加模块与原有系统技术栈不同,同时要整合在一起呈现。年初刚好在项目中,有过一次qiankun接入的经验,所以在拿到这个需求的时候,我直接给领导提供了qiankun接入的方案建议,在研究完umi的技术原理后,就投入到了紧锣密鼓的开发中。

二、umi2接入qiankun

umi的插件机制,为项目的可扩展性带来了无限可能,qiankun提供微前端的服务能力,加之,umi和qiankun都源自蚂蚁金服团队,在技术的实现上,规避了兼容性,因此给此次项目接入也带来了可能,但在查阅资料之后,我发现目前网络上的很多方案都是针对umi3接入qiankun2的,针对umi2的例子很少,由于老系统模块较多,我也没想过升级,所以还是按照umi2去对接。

2.1 umi2+qiankun1

qiankun针对umi开发了umi-plugin-qiankun的插件,与umi2适配的是qiankun1,git上的这篇文章做了详细的说明,大家可参阅。接入步骤如下:

  • 安装 umi-plugin-qiankun1
    npm install @umijs/plugin-qiankun@1 -D
  • config.js文件中,配置微应用具体属性
[
  '@umijs/plugin-qiankun',
  {
    master: {
      apps: [
        {
          mountElementId: 'root-subapp', // 挂载的微应用dom节点
          name: 'audit-model-test', // 微应用名称
          entry: '//10.54.48.141:8989/web/test', // 微应用入口地址
          base: '/web/test', // app1 的路由前缀,通过这个前缀判断是否要启动该应用,通常跟微应用的base保持一致 
          history: 'browser',
        },
      ],
      jsSandbox: true, // 是否启用 js 沙箱,默认为 false
      prefetch: true, // 是否启用 prefetch 特性,默认为 true
    },
  }
]

这里,需要重点介绍一下当前app属性,与微应用配置属性的相互对应性

    name   ------------> 对应的是微应用的名称,和微应用package.json中的name属性一致
    entry  ------------> 微应用启动后的访问地址
    base   ------------> 微应用的路由前缀,非常重要,这里是启动微应用关键,对应的是微应用router对象中的base属性,可以具体查看微应用中路由前缀具体是啥
    history -----------> 需要和微应用路由模式保持一致,否则会加载不上
  • 创建微应用挂载的容器组件 在该容器组件中,需要命名挂载节点,这个节点id,需要和config.js文件中的,mountElementId属性对应。比如下面的root-subapp对应的是上方的mountElementId属性。
<div className="sub-app-container" id="root-subapp">
     微应用
</div>
  • route.js路由文件中,修改路径 路由文件,提供了菜单与跳转路径的对应关系,在路由配置中,除了配置指向微应用的地址路径之外,还需要指定微应用的挂载容器,通常是一个单独的页面组件。
{
  name: '某菜单', path: '/web/test', icon: 'line-chart',
  routes: [
    {
      path: '/web/test/model/establish',  
      name: '某二级菜单',
      component: './subAppContainer',
    }
   ]

至此,umi2中的微应用接入配置已完毕,启动umi2项目,却出现了以下问题。

2.2 umi2中出现的典型问题

  • AssertionError [ERR_ASSERTION]: Document src\pages\document.ejs must contain
    由报错信息可以看到,是找不到挂载节点root-master了。但我们项目的根节点默认是root,怎么会出来root-master呢,原来,配置完qiankun之后,会默认将原来div的东西分配到root-master中,如果页面中,没有root-master将会出现问题。这个解决方案,第一种是将原来的root id修改成root-master,但这显然不是解决问题的方案,将默认的dom id改了之后,可能会引发其他问题。另一种,则是在config.js文件的根节点中,配置 mountElementId: 'root',这样就解决了当前问题。

image.png

export default {
   mountElementId: 'root', // 这里的mountElementId属性和qiankun微应用中的不一样,微应用中指的是挂载微应用的div节点
}
  • [qiankun]: example wrapper with id qiankun_subapp_wrapper_for_example not ready!

第二个问题,则是关于第一次加载微应用成功,第二次切换到其他路由再跳转回来后,就会出现空白页面。 这个问题,qiankun作者给出了解释,这里引用这段说明。

由于路由及事件都是异步的,这种场景下很难确保微应用的 mount 已经开始的时候,对应路由的 Route 组件是已经渲染完毕的,导致由于微应用找不到挂载点而抛异常的问题出现。

三、umi2升级+接入qiankun

由于第2个问题的存在,导致使用umi2接入qiankun方案,陷入了死胡同,刚好在issues找到了答案,作者也回复在qinkun2中进行了升级。那么,问题来了,qiankun1的适配版本是umi2qiankun2以上版本对应的则是umi3,因此,umi2不得不升级版本对应信息

在介绍升级工作前,简单讲一下umi3与umi2最大的不同在哪儿,从安装依赖角度来说,umi3移除了部分依赖的显性安装,如dva,umi-plugin-ga/react等,也就是说不再需要单独install,umi会根据框架的依赖,自动安装插件;从配置的角度来说,umi3不再有插件集合的配置工作,所有插件均以拍平的方式,配置在config文件中。了解这两点,对整个升级工作非常重要,升级参考官网,官网上的介绍相对简略,下面讲一下在项目中的实际升级经验。

3.1 umi2升级工作

  • 卸载

    umi3,会自动注册依赖中的插件,这意味着之前单独install的插件,在umi3中都不再需要。所以第一步,需要卸载或删除之前安装的插件,如dva、umi-plugin-ga、umi-plugin-react、umi-plugin-pro、umi-plugin-pro-block等,可根据自己项目去卸载。除此之外,umi2也需要卸载。npm uninstall 插件或者直接在package.json中删除插件。

  • 安装

    安装umi3,@umijs/preset-react。npm install 插件

  • 配置

    umi3的配置方式,全部以拍平方式进行, umi2中,将插件相关内容,都放在插件数组中单独配置,在umi3中不再采用这种方式。 config.js具体配置如下:

// 插件内容
const plugins = [ // 之前umi2的配置方式,umi3中将删除
    [
    'umi-plugin-react',
    {
      antd: true,
      dva: {
        hmr: true,
      }
    },
]
export default {
    - plugins, // 显示的插件数组,删除不再使用
    + antd: {}, // 将插件中的属性,全部提出,配置在单独对象中
    + dva: {}
    根据官网提示,对照配置文件,删除已不再需要的属性,如在当前项目中,使用了cssLoaderOptions属性,需要替换成cssLoader
    - cssLoaderOptions: {},
    + cssLoader: {}

页面文件替换:

在代码层面,umi3也做了部分修改,如umi/...* 的接口已不再适用。因此全部需要替换

- import Router from 'umi/router'
+ import { history } from 'umi'

3.2 umi3升级报错

升级过程中,一般遇到的报错就是版本不兼容,这里往往是由于package.lock文件中锁定了之前版本信息,导致当前安装的版本与历史版本不兼容,引发了报错。因此在遇到版本信息错误时,仔细核对package.lock文件的版本信息,然后删除node_modules文件夹和package.lock文件,重新安装。这里提供快速删除node_modules文件夹的方法。

    npm install rimraf -D 安装rimraf工具包
    rimraf node_modules 删除命令,记得进入正确的文件夹目录

3.3 umi3接入qiankun

升级成功之后,下面可以进行qiankun的接入工作。

  • 安装 @umijs/plugin-qiankun
  • config.js文件配置
export default {
  mountElementId: 'root',
  qiankun: {
    master: {
      apps: [
        {
          name: 'audit-model-test', // 只需要配置名称和入口,与微应用的相关对应方式,查看上面介绍
          entry: '//10.54.48.141:8989/web/test',
        }
      ]
    }
  }
}
  • router.js文件配置
{
  name: '某菜单', path: '/web/test', icon: 'line-chart',
  routes: [
    { path: '/web/test/model/establish',
      name: '某二级菜单',
      microApp: 'audit-model-test', // 不再采用组件component引入的方式,采用microApp的形式
    },
  • 微应用二级路由,可在router.js文件中配置 hideInMenu,注意:,这里的path一定要和微应用中的跳转二级路由保持一致,同时要注意和父路由path根路径保持一致,否则会出现404无法找到页面的问题。
umi项目中的router.js文件
routes: [
    { path: '/web/test/model/establish'}
    { // 微应用二级路由的配置方式,如从列表页,跳转至详情页
      hideInMenu: true,
      path: '/web/test/model/establish/view/:id', // 这里的根路径,需要和path保持一致,同时和微应用中的跳转路由一致。
    }
]

微应用项目中的跳转路径
this.$router.push({
   path: `/model/establish/${action}/${record.id}`,
})

  • 指定渲染位置 可以通过react组件渲染微应用,从而指定渲染位置。新建subAppContainer.jsx文件,在文件中通过microApp指定微应用渲染容器。
import { MicroApp } from 'umi'
const SubAppContainer = () => (
   <div class="micro-app">
    <MicroApp name="audit-model-test" />
   </div>
)

四、接入效果

  • 微应用列表页 image.png
  • 微应用的详情页 image.png

五、总结

本文记录了使用umi不同版本接入qiankun时,遇到的问题和解决过程。

通过这篇文章,你将了解到什么呢?

  • umi2如何接入qiankun,以及遇到了哪些典型问题
  • umi2如何升级到umi3,两个版本有何不同
  • umi3如何接入qiankun,接入效果是哪样的

目前项目中,已顺利接入qiankun,但在浏览器返回操作时,遇到了路由混乱问题,该问题目前还在解决中。解决完之后,会补充到这篇文章中。