前言:
问题:
(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
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby1.9+ 关于ruby-主要:Objectwhenrun
在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList()Obt
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/
作为新的阿里云用户,您可以50免费试用多种优惠,价值高达1,700美元(或8,500美元)。这将让您了解和体验阿里云平台上提供的一系列产品和服务。如果您以个人身份注册免费试用,您将获得价值1,700美元的优惠。但是,如果您是注册公司,您可以选择企业免费试用,提交基本信息通过企业实名注册验证,即可开始价值$8,500的免费试用!本教程介绍了如何设置您的帐户并使用您的免费试用版。关于免费试用在我们开始此试用之前,您还必须遵守以下条款和条件才能访问您的免费试用:只有在一年内创建的账户才有资格获得阿里云免费试用。通过此免费试用优惠,用户可以免费试用免费试用活动页面上列出的每种产品一次。如果您有多个帐
我的ruby脚本从命令行参数获取某些输入。它检查是否缺少任何命令行参数,然后提示用户输入。但是我无法使用gets从用户那里获得输入。示例代码:test.rbname=""ARGV.eachdo|a|ifa.include?('-n')name=aputs"Argument:#{a}"endendifname==""puts"entername:"name=getsputsnameend运行脚本:rubytest.rbraghav-k错误结果:test.rb:6:in`gets':Nosuchfileordirectory-raghav-k(Errno::ENOENT)fromtes
关闭。这个问题需要更多focused.它目前不接受答案。想改进这个问题吗?更新问题,使其只关注一个问题editingthispost.关闭9年前。Improvethisquestion我现在是java专业人士,我喜欢使用ruby。这两种语言有什么相似之处吗?主要区别是什么?因为两者都是面向对象的。
我是Ruby分析的新手,看起来像ruby-prof是一个受欢迎的选择。我刚刚安装了gem并调用了我的程序:ruby-prof./my-prog.rb但是,输出非常冗长,因为包含所有Ruby核心和标准库方法以及其他gem的分析数据。例如,前三行是:8.790.0110.0100.0000.0013343*String#%7.280.0780.0090.0000.0692068*Array#each4.930.0380.0060.0000.0321098*Array#map这对我来说不是什么有用的信息,因为我已经知道我的程序经常处理字符串和数组,并且大概已经对这些类进行了优化。我只关心我代
我正在使用Rails并且非常随机地遇到连接池错误,它不专门针对任何单个端点。我可以在大约70%的时间内命中端点而不会出现此错误。数据库是在谷歌云上运行的PostgreSQL。这是我遇到的错误的主要内容:#/usr/local/bundle/gems/activerecord-5.1.5/lib/active_record/connection_handling.rb:112:in`connection_pool'ActiveRecord::ConnectionNotEstablished(Noconnectionpoolwith'primary'found.):gem文件:source
在Rails3.x应用程序中,我正在使用net::ssh并向远程pc运行一些命令。我想向用户的浏览器显示实时日志。比如,如果两个命令在net中运行::ssh执行即echo"Hello",echo"Bye"被传递然后"Hello"应该在执行后立即显示在浏览器中。这是代码我在rubyonrails应用程序中使用ssh连接和运行命令Net::SSH.start(@servers['local'],@machine_name,:password=>@machine_pwd,:timeout=>30)do|ssh|ssh.open_channeldo|channel|channel.requ