前端微服务-qiankun从0到1搭建(主要核心点)以及需要注意的地方

记录前端微服务-qiankun从0到1搭建(主要核心点)需要注意的地方

前言:

  • 由于我们公司的项目基本都是后台管理系统类的,所以要求微应用入口必须是主应用的一个路由菜单、而且需要处理菜单权限、信息共享等问题、但是qiankun官方文档中案例记录中使用的都是registerMicroApps方法来进行引入子应用的配置、导致项目刚开始的时候踩了很多坑。所以在此记录一下一些需要注意的点。文章末尾有自己搭建的简易的主应用模板、以及微应用模板、后续需要使用可以直接clone下来,稍微改改就能用了。

问题:
(1). 使用registerMicroApps注册微应用的时候,无法解决路由缓存、子应用动态路由(权限)等一系列问题。
解决方案:
改成使用loadMicroApp方法来动态加载子应用、在主应用某个菜单的mounted生命周期中去load子应用,然后当这个菜单组件销毁的时候随之将子应用销毁。此处我是直接将其封装成了公用组件、其他所有子应用都是用这个组件来映射。
子应用挂载的组件
代码:(/layout/components/microAppView.vue)
此处使用keep-alive标签来保持子应用活性、防止切换不同子应用时,重新创建子应用、导致缓存销毁。

<template>
  <keep-alive>
    <component :is="microAppMap[showApp]"></component>
  </keep-alive>
</template>
<script lang="ts">
export default {
  name: "MicroAppView"
};
</script>
<script lang="ts" setup>
import { computed } from "vue";
import { getConfigByPath } from "@/plugin/qiankun/index";
import { useRoute } from "vue-router";
import homeApp from "./microApp/homeApp.vue";
import vueApp from "./microApp/vueApp.vue";
import { GeneraDataType } from "@/interface";
const route = useRoute();
const microAppMap: GeneraDataType = {
  "/app-vue/": vueApp,
  "/home-vue/": homeApp
};
const showApp = computed<string>(() => {
  const { configKey } = getConfigByPath(route.path);
  return configKey;
});
</script>

子应用-挂载组件(由于多子应用缓存问题、不同子应用需要创建不同的组件)
统一存放在 (/layout/microApp/.vue)
例子: /layout/microApp/
.vue

<script lang="ts">
export default {
  name: "HomeApp"
};
</script>
<script lang="ts" setup>
import { onMounted, onUnmounted } from "vue";
import InitHook from "../composables/microAppView/initHook";
import microAppRender from "../microAppRender.vue";
const { microApp, pageLoadingOpts, resetHandle } = InitHook();
onMounted(() => {
  resetHandle();
});
onUnmounted(() => {
  microApp.value.unmount();
});
</script>
<template>
  <a-spin :spinning="pageLoadingOpts.loading" :tip="pageLoadingOpts.loadingText">
    <microAppRender container-id="home-vue" :page-loading-opts="pageLoadingOpts" @reload="resetHandle"></microAppRender>
  </a-spin>
</template>

上述封装 InitHook 函数用于加载微应用、代码存放在(layout/components/composables/microAppView)目录下

该hook函数中、加载微应用时、通过将主应用的router对象传递给子应用、从而实现不同微应用之间相互跳转、同时将token传递给子应用、将登录状态共享

import { reactive, ref } from "vue";
import { PageLoadingSet } from "../../../type";
import { loadMicroApp, getConfigByPath, getMicroAppRouteList, getMicroAppRouteCache } from "@/plugin/qiankun/index";
import { useRoute, useRouter } from "vue-router";
export default function InitHook() {
  const route = useRoute();
  const router = useRouter();
  const microApp = ref<any>(null);
  const pageLoadingOpts = reactive<PageLoadingSet>({
    loading: false,
    loadingText: "玩命加载中...",
    errorText: ""
  });
  const resetHandle = () => {
    const { config, configKey } = getConfigByPath(route.path);
    pageLoadingOpts.loading = true;
    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().finally(() => {
          pageLoadingOpts.loading = false;
          pageLoadingOpts.errorText = "";
        });
      })
      .catch(() => {
        pageLoadingOpts.loading = false;
        pageLoadingOpts.errorText = "微应用加载失败、请刷新页面重新加载!";
      });
  };
  return {
    pageLoadingOpts,
    microApp,
    resetHandle
  };
}

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: "#app-vue",
    activeRule: "/app-vue/" // 子应用app的触发路由(路径)
  },
  "/home-vue/": {
    name: "qiankun-home",
    entry: "//localhost:9999",
    container: "#home-vue",
    activeRule: "/home-vue/" // 子应用home的触发路由(路径)
  }
};


这里我处理成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) {
  // 如果作为微应用运行 则不劫持路由 防止与主应用的路由冲突
  if (window.__POWERED_BY_QIANKUN__) {
    MicroAppAction.onGlobalStateChange((state: any, prev: any) => {
      console.log("子应用接收到全局状态变更信息", state, prev);
      // 动态添加缓存
      store.dispatch("INIT_CACHEPAGE_LISTACTION", state.cachePageList);
      console.log("子应用的缓存列表", store.state.cachePageList);
      store.dispatch("CHANGE_CURRENTPATH_ACTION", state.currentPath);
    });
  } else {
    router.beforeEach(async (to, from, next) => {
      // 微应用
      if (window.sessionStorage.getItem("token")) {
        // 单独运行
        // 发送http请求 获取权限菜单
        if (!hasRole) {
          // 此处模拟用写死的数据代替后端返回的数据
          const addRoutes = addAsyncRoute(asyncRoutes);
          addRoutes.forEach((t: RouteRecordRaw) => router.addRoute(t));
          hasRole = true;
          router.push(to.path);
        } else {
          // 动态添加缓存
          if (to.name && !store.state.cachePageList.includes(to.name)) {
            await store.dispatch("SET_CACHEPAGE_LISTACTION", to.name);
          }
        }
        next();
      } else {
        // 此处没有权限跳转到登陆页面
        if (!hasRole) {
          // 此处模拟用写死的数据代替后端返回的数据
          const addRoutes = addAsyncRoute(asyncRoutes);
          addRoutes.forEach((t: RouteRecordRaw) => router.addRoute(t));
          hasRole = true;
          router.push(to.path);
        } else {
          // 动态添加缓存
          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

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值