前言:
问题:
(1). 使用registerMicroApps注册微应用的时候,无法解决路由缓存、子应用动态路由(权限)等一系列问题。
解决方案:
改成使用loadMicroApp方法来动态加载子应用、在主应用某个菜单的mounted生命周期中去load子应用,然后当这个菜单组件销毁的时候随之将子应用销毁。此处我是直接将其封装成了公用组件、其他所有子应用都是用这个组件来映射。
子应用挂载的组件
代码:(/layout/components/microAppView.vue)
此处id = container 这个其他所有微应用配置的挂载节点都填’#container’
将子应用的权限路由 通过props传递给子应用,从而解决子应用权限路由的问题
<template>
<div id="container"></div>
</template>
<script lang="ts" setup>
import { onMounted, onUnmounted, ref } from "vue";
import {
loadMicroApp,
getConfigByPath,
getMicroAppRouteList,
getMicroAppRouteCache,
} from "@/plugin/qiankun/index";
import { useRoute, useRouter } from "vue-router";
console.log("渲染子应用");
const microApp = ref<any>(null);
onMounted(() => {
const route = useRoute();
const router = useRouter();
const { config, configKey } = getConfigByPath(route.path);
console.log("获取注册微应用缓存列表==>", getMicroAppRouteCache(configKey));
console.log("微应用路由列表", getMicroAppRouteList(configKey));
microApp.value = loadMicroApp({
...config,
props: {
cachePageList: getMicroAppRouteCache(configKey),
asyncRoutes: getMicroAppRouteList(configKey),
mainRouter: router,
token: window.sessionStorage.getItem("token"),
},
});
microApp.value.unmount().then(() => {
microApp.value.mount();
console.log("装载");
});
});
onUnmounted(() => {
console.log("组件卸载");
microApp.value.unmount();
});
</script>
qiankun微应用配置
代码路径(‘/plugin/qiankun/app.ts’)
import { MicroAppType } from "./type";
import { GeneraDataType } from "@/interface";
export const microappMap: GeneraDataType<MicroAppType.MicroAppRow> = {
"/app-vue/": {
name: "qiankun-app", // app name registered
entry: "//localhost:7777",
container: "#container",
activeRule: "/app-vue/", // 子应用app的触发路由(路径)
},
};
这里我处理成key-value的形式,是为了多个微应用、通过不同的route path来匹配不同的配置,从而加载不同的微应用
(2)如何实现微应用之间互相跳转
解决方案:上述代码中将主应用的router(实现微应用之间互相跳转)通过qiankun的loadMicroApp加载微应用时传递给微应用,在微应用中需要跳转到其他微应用时,使用主应用的router对象来跳转。
(3). 子应用单独运行时,和作为微应用运行时如何区分权限路由、和路由跳转?
解决方案:
通过路由守卫、劫持然后判断全局变量__POWERED_BY_QIANKUN__ 来区分单独运行还是作为微应用运行。
代码路径:(/qiankun-vue3-ts-app/router/initRouter.ts)(注意此处代码是子应用的代码)
import { Router, RouteRecordRaw } from "vue-router";
import store from "@/store/index";
import { asyncRoutes } from "./data";
import { addAsyncRoute } from "@/router/utils";
// 判断当前权限路由是否已经初始化了
let hasRole = false;
export default function InitRouter(router: Router) {
router.beforeEach(async (to, from, next) => {
// 微应用
if (window.__POWERED_BY_QIANKUN__) {
// 获取主应用传递过来的菜单 此处不进行路由拦截、防止和主应用冲突
} else {
// 单独运行
// 发送http请求 获取权限菜单
if (!hasRole) {
// 此处模拟用写死的数据代替后端返回的数据
const addRoutes = addAsyncRoute(asyncRoutes);
addRoutes.forEach((t: RouteRecordRaw) => router.addRoute(t));
hasRole = true;
router.push(to.path);
}
}
// 动态添加缓存
if (to.name && !store.state.cachePageList.includes(to.name)) {
await store.dispatch("SET_CACHEPAGE_LISTACTION", to.name);
}
next();
});
router.afterEach(() => {
console.log("子应用路由守卫结束");
});
}
其他需要注意的点:
(1). 在做前端路由工程化的时候(通过文件目录动态引入组件,不写死),需要注意子应用作为微应用运行时,路由配置base的时候会加前缀所以路由跳转的时候都是默认基于base配置的路径来跳转的,所以需要去除主应用传递过来的路由的前缀,我们写一个正则把前缀去除就好了。
(2). 作为微应用执行时子应用的路由注册的时候也需要将主应用传递过来的路由前缀去除(我这里是/app-vue/)
比较重要的就上面所说的几点、其他的可以git clone我的代码、里面都有注释、在这里就不多一一赘述了。
结尾:
其他上面代码中引入的方法,都可以在项目的目录中找到、代码中都有注释、这里贴上代码的git地址:有需要的可以自取
主应用: https://gitee.com/zhong-wenkai/qiankun-vue3-ts-base.git
微应用: https://gitee.com/zhong-wenkai/qiankun-vue3-ts-app.git