不瞒大家说,一分钟前,我还在准备软考,结果,收到了软考延期的通知。最近很忙,一边负责项目方面的工作,一边备考,前几天就想要写一写最近碰到的问题,因为备考就无限期的延期了。好在当下,软考取消了,可以有时间来为前几天的工作做个总结了。
一、聊聊项目现状
老生常谈,在讲技术实现前,先聊一聊项目现状。项目开始于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',这样就解决了当前问题。
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的适配版本是umi2,qiankun2以上版本对应的则是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>
)
- 微应用接入案例,可参考之前的文章「微前端实践」使用Vue+qiankun微前端方案重构老项目的本地验证
四、接入效果
- 微应用列表页
- 微应用的详情页
五、总结
本文记录了使用umi不同版本接入qiankun时,遇到的问题和解决过程。
通过这篇文章,你将了解到什么呢?
- umi2如何接入qiankun,以及遇到了哪些典型问题
- umi2如何升级到umi3,两个版本有何不同
- umi3如何接入qiankun,接入效果是哪样的
目前项目中,已顺利接入qiankun,但在浏览器返回操作时,遇到了路由混乱问题,该问题目前还在解决中。解决完之后,会补充到这篇文章中。