微前端集成时子应用接入改造
自有框架改造,有一定难度,不推荐使用
简单集成时,只需修改以下两点
- 子应用生命周期改造,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");
}
- 解决跨域,vue+vite时,在vite.config.ts中如下修改
server: {
...
cors: true,
...
},
集成案例
upms的自我集成,upms基于vue3+vite2.9+ts开发
- 自我集成时,缓存通用,可不做免密登录,可省略。
- 生命周期改造,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();
}
- 修改布局文件,根据项目需求,判断在微前端情况下时,隐藏左侧、顶部菜单
- 在子应用的默认首页添加消息发送事件,表明此时路由等组件已创建完成,可接收主应用的跳转指令。
ts
window.$wujie?.bus.$emit('onMicroMessage', '子应用发送事件啦');
- 添加监听事件,接收主应用传递来的路由跳转指令,在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;
- 在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开发
- 需要做生命周期改造,代码参考upms。
- 需要做免密登录
(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" />
- 解决跨域:参考vite
- 添加路由控制
(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开发
- 生命周期改造
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}
});
}
- 添加子应用创建成功后,向主应用发送通知
views/index.vue
mounted() {
if (window.__POWERED_BY_WUJIE__) {
// 子应用发送事件
window.$wujie.bus.$emit('onMicroMessage', '子应用发送事件啦');
}
},
- 添加消息监听,执行路由跳转、把通信数据保存在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
- 解决跨域vue.config.js
devServer: {
headers: {
'Access-Control-Allow-Origin': '*',
},
},
- 免密登录
- 菜单权限