Skip to content
当前页

微前端集成时子应用接入改造

自有框架改造,有一定难度,不推荐使用

简单集成时,只需修改以下两点

  1. 子应用生命周期改造,vue+vite时,在main.ts中如下修改(非保活模式可不修改)
declare global {
  interface Window {
    // 是否存在无界
    __POWERED_BY_WUJIE__?: boolean;
    // 子应用mount函数
    __WUJIE_MOUNT: () => void;
    // 子应用unmount函数
    __WUJIE_UNMOUNT: () => void;
    // 子应用无界实例
    __WUJIE: { mount: () => void };
  }
}

if (window.__POWERED_BY_WUJIE__) {
  let instance: any;
  window.__WUJIE_MOUNT = () => {
    const router = createRouter({ history: createWebHistory(), routes });
    instance = createApp(App)
    instance.use(router);
    instance.mount("#app");
  };
  window.__WUJIE_UNMOUNT = () => {
    instance.unmount();
  };
  /*
    由于vite是异步加载,而无界可能采用fiber执行机制
    所以mount的调用时机无法确认,框架调用时可能vite
    还没有加载回来,这里采用主动调用防止用没有mount
    无界mount函数内置标记,不用担心重复mount
  */
  window.__WUJIE.mount()
} else {
  createApp(App).use(createRouter({ history: createWebHistory(), routes })).mount("#app");
}
  1. 解决跨域,vue+vite时,在vite.config.ts中如下修改
server: {
    ...
    cors: true,
    ...
},

集成案例

upms的自我集成,upms基于vue3+vite2.9+ts开发

  1. 自我集成时,缓存通用,可不做免密登录,可省略。
  2. 生命周期改造,main.ts做如下修改。
ts
import { Component, createApp } from 'vue';
import { registerGlobComp, registerGlobPlugin } from './components/index';
import App from './App.vue';
import { installStore } from './stores';
import router, { installRouter } from './router';
import { usI18n } from '/@/utils/i18n';
// import '/@/router/permission';
import 'virtual:svg-icons-register';
import 'virtual:windi.css';

// css

// import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import '/@/styles/index.css';
import { installRouterGuard } from '/@/router/guard';
import { initSettingsStore } from '/@/settings/initSettings';
import { MotionPlugin } from '@vueuse/motion';
import { installGlobDirectives } from '/@/directives';
import mitt from 'mitt';
import * as ElementPlusIconsVue from '@element-plus/icons-vue';
import createEvent from './views/micro/libs/messageEvent';

let app: any;
async function bootstrap() {
  app = createApp(App);
  // 注册图标
  for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, <Component>component);
  }
  app.config.globalProperties.$mitt = mitt();
  app.use(MotionPlugin);
  // app.use(ElementPlus);
  // 注册响应式配置信息(layout, theme等)
  initSettingsStore();
  // install directives
  installGlobDirectives(app);

  // install store
  installStore(app);

  // install routing
  installRouter(app);

  // 加载全局组件
  registerGlobComp(app);

  // 加载全局插件
  registerGlobPlugin(app);

  // 注册路由守卫
  installRouterGuard(router);
  // 国际化
  await usI18n(app);

  app.mount('#app');
}

// bootstrap();
declare global {
  interface Window {
    // 是否存在无界
    __POWERED_BY_WUJIE__?: boolean;
    // 子应用mount函数
    __WUJIE_MOUNT: () => void;
    // 子应用unmount函数
    __WUJIE_UNMOUNT: () => void;
    // 子应用无界实例
    __WUJIE: { mount: () => void };
    $wujie: {
      bus: any;
      shadowRoot?: ShadowRoot | undefined;
      props?: { [key: string]: any } | undefined;
      location?: Object;
    };
  }
}

if (window.__POWERED_BY_WUJIE__) {
  createEvent();
  window.__WUJIE_MOUNT = () => {
    bootstrap();
  };
  window.__WUJIE_UNMOUNT = () => {
    console.log('__WUJIE_UNMOUNT');
    app.unmount();
  };
  /*
    由于vite是异步加载,而无界可能采用fiber执行机制
    所以mount的调用时机无法确认,框架调用时可能vite
    还没有加载回来,这里采用主动调用防止用没有mount
    无界mount函数内置标记,不用担心重复mount
  */
  window.__WUJIE.mount();
} else {
  bootstrap();
}
  1. 修改布局文件,根据项目需求,判断在微前端情况下时,隐藏左侧、顶部菜单
  2. 在子应用的默认首页添加消息发送事件,表明此时路由等组件已创建完成,可接收主应用的跳转指令。
ts
window.$wujie?.bus.$emit('onMicroMessage', '子应用发送事件啦');
  1. 添加监听事件,接收主应用传递来的路由跳转指令,在main.ts中调用此函数。
ts
import router from '../../../router';

const createEvent = () => {
  window.$wujie?.bus.$on('sendMicroMessage', function (data) {
    console.log('子应用接收到的数据', data.message.path);
    if (data.type == 'router' && data.message.path) {
      router.push(data.message.path as string);
    }
  });
};
export default createEvent;
  1. 在upms的主应用中配置菜单:类型选微应用、地址填写完整的线上/开发地址、名称不可与其他微应用重复(同一个微应用的名称固定,开发/线上需区分名称)、路由写想要跳转的子应用页面的路由。
ts
{
    id: '1563044314266959873',
    parentId: '1560521273460367362',
    children: [],
    label: 'vite菜单',
    status: null,
    name: 'vite菜单',
    path: '/micro/vite',
    component: null,
    type: '2',
    sort: 3,
    meta: {
        hideMenu: '0',
        icon: '',
        microType: 'vite',
        title: 'vite菜单',
        url: 'http://localhost:4173/',
        microPath: '/upms/menu',
        microName: 'upms',
    },
}

bpm的集成,bpm基于vue3+vite2.6+ts开发

  1. 需要做生命周期改造,代码参考upms。
  2. 需要做免密登录
(1)在登录页面添加:子应用发送创建成功通知、子应用监听主应用传递的token并执行登录、保存主应用传递的token、主动发通信告知主应用,需要token。
cpit-bpm-ui\src\views\sys\login\LoginForm.vue
if (window.__POWERED_BY_WUJIE__) {
    window.$wujie?.bus.$on('sendMicroMessage', function (data) {
      console.log('login子应用接收到的数据', data);
      if (data.type === 'login') {
        microLogin(data.message);
        userStore.setMainToken(data.message.token);//maintoken 相关的三个文件:cpit-bpm-ui\src\enums\cacheEnum.ts、cpit-bpm-ui\src\store\modules\user.ts、cpit-bpm-ui\src\utils\cache\persistent.ts
      }
    // 子应用发送事件
    window.$wujie?.bus.$emit('onMicroMessage', { type: 'beforeLogin', message: 'bpm子应用创建成功' });
}
(2)控制登录页在微前端时不显示
cpit-bpm-ui\src\views\sys\login\Login.vue
<div :class="prefixCls" class="relative w-full h-full px-4" v-show="!isMicro">
const isMicro = window.__POWERED_BY_WUJIE__;
(3)添加免密登录的新接口
cpit-bpm-ui\src\api\sys\user.ts
(4)隐藏用户组件
cpit-bpm-ui\src\layouts\default\header\index.vue
<UserDropDown v-if="!isMicro" :theme="getHeaderTheme"  />
  1. 解决跨域:参考vite
  2. 添加路由控制
(1)添加监听路由跳转的指令和登录指令
cpit-bpm-ui\src\App.vue
  import { router } from '/@/router';
  import { useUserStore } from '/@/store/modules/user';
  // 子应用监听事件
  if (window.__POWERED_BY_WUJIE__) {
    const userStore = useUserStore();
    window.$wujie?.bus.$on('sendMicroMessage', function (data) {
      console.log('app-bpm接收到的数据', data);
      if (data.type == 'router' && data.message.path) {
        router.push(data.message.path as string);
      }
      if (data.type === 'logout') {
        userStore.logout();
      }
    });
  }
  (2)修改配置
  cpit-bpm-ui\src\settings\projectSetting.ts
  菜单和标签页在微前端时不显示
  (3)当子应用有缓存时,不走登录页,需要在默认首页向主应用发送创建成功通知。
  cpit-bpm-ui\src\views\dashboard\home\index.vue
  if (window.__POWERED_BY_WUJIE__) {
    // 子应用发送事件,不填类型,默认走子应用创建成功
    window.$wujie?.bus.$emit('onMicroMessage', { message: 'bpm子应用登录成功' });
  }

teamwork的集成,teamwork基于vue2+webpack+js开发

  1. 生命周期改造
main.js
// console.log('props', props);
if (window.__POWERED_BY_WUJIE__) {
    let instance;
    window.__WUJIE_MOUNT = () => {
      instance = new Vue({
        el: '#app',
        store,
        router,
        template: '<App/>',
        components: {App}
      });
      // const props = window.$wujie?.props; // {data: xxx, methods: xxx}
      console.log('props', window);
    };
    window.__WUJIE_UNMOUNT = () => {
      instance.$destroy();
    };
  } else {
    // new Vue({ router: new VueRouter({ routes }), render: (h) => h(App) }).$mount("#app");
    new Vue({
        el: '#app',
        store,
        router,
        template: '<App/>',
        components: {App}
    });
}
  1. 添加子应用创建成功后,向主应用发送通知
views/index.vue
mounted() {
    if (window.__POWERED_BY_WUJIE__) {
        // 子应用发送事件
        window.$wujie.bus.$emit('onMicroMessage', '子应用发送事件啦');
    }
},
  1. 添加消息监听,执行路由跳转、把通信数据保存在vuex中
app.vue
mounted() {
    console.log('teamwork mounted');
    if (window.__POWERED_BY_WUJIE__) {
      window.$wujie.bus.$on('sendMicroMessage', (data) => {
        console.log('teamwork接收到的数据', data);
        this.$store.commit('setMicroData', data);
        if (data.type == 'router' && data.message.path) {
          this.$router.push(data.message.path);
        }
        console.log('this.$store.',this.$store.state.microData);
      });
    }
},
vuex中添加microData属性和保存方法
cpit-teamwork-ui\src\store\state.js
  1. 解决跨域vue.config.js
devServer: {
    headers: {
        'Access-Control-Allow-Origin': '*',
    },
},
  1. 免密登录
  2. 菜单权限