微前端引入日记

62 阅读3分钟

遇到的问题

提前引申一下,在引入微前端之后遇到的一些问题

引入micro-app之后遇到的问题: # micro-app环境下,子系统加载vxe-table样式被吞解决方法

引入微前端的背景

每个项目有各自的域名,但是使用同一套token,切换登录麻烦

对用户来说入口过多,也不方便使用

技术选型

京东出品 micro-app

接入过程

新起一个单点登录系统

单点登录(SingleSignOn,SSO) 构成很简单,登陆各系统入口平铺 && 上方导航菜单

更精细一点的还可以在进入系统之前,查看系统功能简介

在主应用引入 micro-app

main.ts

-disable-memory-router:默认情况下,子应用将运行在虚拟路由系统中,和主应用的路由系统进行隔离,避免相互影响。

子应用的路由信息会作为query参数同步到浏览器地址上,如下:

alt

设置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();