遇到的问题
提前引申一下,在引入微前端之后遇到的一些问题
引入micro-app之后遇到的问题: # micro-app环境下,子系统加载vxe-table样式被吞解决方法
引入微前端的背景
每个项目有各自的域名,但是使用同一套token,切换登录麻烦
对用户来说入口过多,也不方便使用
技术选型
京东出品 micro-app
接入过程
新起一个单点登录系统
单点登录(SingleSignOn,SSO)
构成很简单,登陆
、各系统入口平铺 && 上方导航菜单
更精细一点的还可以在进入系统之前,查看系统功能简介
在主应用引入 micro-app
main.ts
-disable-memory-router:默认情况下,子应用将运行在虚拟路由系统中,和主应用的路由系统进行隔离,避免相互影响。
子应用的路由信息会作为query参数同步到浏览器地址上,如下:
设置
disable-memory-router
后,子应用将基于浏览器的路由系统进行渲染,拥有更加简洁优雅的的浏览器地址,详情参考虚拟路由系统。
-disable-patch-request:默认情况下,MicroApp对子应用的fetch、XMLHttpRequest、EventSource进行重写,当请求相对地址时会使用子应用域名自动补全
如:
fetch('/api/data')
补全为fetch(子应用域名 + '/api/data')
如果不需要这样的补全,可以配置
disable-patch-request
进行关闭,此时相对地址会兜底到主应用域名。如:
fetch('/api/data')
兜底为fetch(主应用域名 + '/api/data')
import microApp from "@micro-zoe/micro-app";
// 启动microApp框架,配置相关参数
microApp.start({
inline: true, // 使用inline模式加载子应用
"disable-memory-router": true, // 关闭虚拟路由系统,避免对子应用的路由冲突
"disable-patch-request": true, // 关闭对子应用请求的拦截,确保子应用的请求可以正常发送
lifeCycles: {
// 定义子应用生命周期钩子函数
created(e, appName) {
// 当子应用被创建时执行的操作
console.log(`子应用${appName}被创建`);
// 移除本地存储中的敏感数据,确保安全性
window.localStorage.removeItem("secretData");
},
},
});
加载子应用
子应用xx.vue
data?: Object, // 传递给子应用的数据,可选
onDataChange?: Function, // 获取子应用发送数据的监听函数,可选
<template>
<micro-app
class="xx-wrapper"
name="xxweb"
:url="url"
baseroute="/xxweb"
:data="microAppData"
@datachange="handleDataChange"
></micro-app>
</template>
<script setup lang="ts">
// 导入 Vue 的计算属性和获取用户存储的钩子
import { computed, unref } from "vue";
import { useUserStore } from "@/store";
// 配置写在外面,单独引入,这里为了方便,随便写写
const config = {
pro:{ xxweb: "http://xxurl/", /* ... */ },
dev:{ /* ... */ }
}
// 计算属性:根据环境动态生成 merakweb 应用的 URL
const url = computed(() =>
import.meta.env.VITE_BUILD_TYPE === "dev"
? `${config.dev.xxweb}`
: `${config.pro.xxweb}`
);
// 获取用户存储实例
const userStore = useUserStore();
// 计算属性:生成微应用所需的用户数据
const microAppData = computed(() => ({
token: unref(userStore.token),
// ...
}));
/**
* 处理数据变更事件
* @param {CustomEvent} e - 自定义事件,包含类型和数据
*/
function handleDataChange(e) {
// 处理事件,如登出、切换部门、切换状态等
// ...
}
</script>
<style scoped>
.xx-wrapper {
height: 100%;
}
</style>
在子应用引入 micro-app
main.js
import "./micro"; // 微前端初始化和监听
micro.js
addDataListener:监听指定子应用的数据变化
clearDataListener:清空当前子应用的所有数据监听函数(全局数据函数除外)
import store from "./store";
/**
* 监听应用数据变化并更新应用状态
* @param {Object} appData - 从基座接收到的应用数据,包含token、用户信息、部门信息等信息
*/
function dataListener(appData) {
// 更新应用状态中的token、用户信息等
store.commit("user/SET_TOKEN", appData.token);
// ...
// 切换部门, 重新加载路由
if (
appData.departmentId &&
appData.departmentId !== store.getters.departmentId
) {
// 如果部门ID发生变化,更新应用状态中的部门ID并刷新页面
store.commit("user/SET_DEPARTMENTID", appData.departmentId);
window.rawWindow.location.reload(); // 刷新页面
}
}
/**
* 与基座进行数据交互
* 在微前端环境下,主动获取基座下发的数据并监听数据变化
*/
function handleMicroData() {
// 是否是微前端环境
if (window.__MICRO_APP_ENVIRONMENT__) {
/* 主动获取基座下发的数据 */
const appData = window.microApp.getData();
// 更新应用状态中的token、用户信息等
store.commit("user/SET_TOKEN", appData.token);
// ... 用户信息等
// 如果应用状态中没有部门ID,更新部门ID
if (!store.getters.departmentId) {
store.commit("user/SET_DEPARTMENTID", appData.departmentId);
}
/**
* https://zeroing.jd.com/micro-app/docs.html#/zh-cn/data
* 绑定监听函数,监听函数只有在数据变化时才会触发
* dataListener: 绑定函数
* autoTrigger: 在初次绑定监听函数时如果有缓存数据,是否需要主动触发一次,默认为false
*/
window.microApp.addDataListener(dataListener, false);
// 在应用卸载时清除数据监听器
window.addEventListener("unmount", function () {
window.microApp.clearDataListener();
});
}
}
// 执行与基座进行数据交互的函数
handleMicroData();