原文
前言
前沿摘抄自 qiankun 官网微前端
demo + source code
问题点
解决 主应用 和 子应用 vue-router 路由实例冲突
基座改造
npm install qiankun -S
1. main.ts 改造
// 导入qiankun 文件
import { loadApps } from './qiankun';
const app = createApp(App);
...
...
// 执行
loadApps();
app.mount('#app');
2. qiankun.ts
import { registerMicroApps, start } from 'qiankun';
import { FrameworkConfiguration } from 'qiankun/es/interfaces.d';
import { useCommonStore } from '@/store';
const config: FrameworkConfiguration = {
prefetch: 'all',
singular: true, // 单实例应用场景
};
const apps = [
{
name: 'vue3', // 必须与微应用注册名字相同
entry: 'http://localhost:4010/vue3', // 入口路径, 上线环境为上线地址
container: '#subAppContainer', // app.vue 配置的挂载容器 id
activeRule: `${import.meta.env.VITE_APP_PUBLIC_PATH}vue3`,
},
];
const loadApps = async () => {
const env = import.meta.env.VITE_NODE_ENV;
const commonStore = useCommonStore();
registerMicroApps(apps);
start(config);
return apps;
};
export { loadApps };
3. App.vue
<template>
<router-view />
</template>
<script setup lang="ts">
import { ConfigProvider } from 'ant-design-vue';
import { watch } from 'vue';
import { useRoute } from 'vue-router';
import { initGlobalState } from 'qiankun';
const route = useRoute();
const actions = initGlobalState({});
// 将 路由信息 通过全局挂载 传递到微服务
watch(
() => route,
val => {
const { path: pathVal } = val;
const [, application, path] = pathVal.split('/');
actions.setGlobalState({
application,
path,
});
},
{
deep: true,
immediate: true,
},
);
</script>
4. router.ts
const routes: RouteRecordRaw[] = [
{
path: '/:pathMatch(.*)*',
component: TheHeader,
children: [],
},
];
// 必须采用 history 模式, hash 模式不可设置为底座
const router = createRouter({
history: createWebHistory(import.meta.env.VITE_APP_PUBLIC_PATH || '/'),
routes,
});
5. TheHeader.vue
<template>
...
<div id="subAppContainer" />
...
</template>
微应用
npm i vite-plugin-qiankun -S
1. main.ts 改造
import { createApp } from 'vue';
import App from './App.vue';
import { router } from './router/index';
import useQiankun from '@/hooks/useQiankun';
import {
renderWithQiankun,
QiankunProps,
qiankunWindow,
} from 'vite-plugin-qiankun/dist/helper';
const render = () => {
const app = createApp(App);
app.use(store);
app.use(router);
app.mount('#container');
};
renderWithQiankun({
mount(props) {
render();
useQiankun(props);
},
bootstrap() {},
unmount(props: any) {},
update: function (props: QiankunProps): void | Promise<void> {},
});
if (!qiankunWindow.__POWERED_BY_QIANKUN__) render();
2. useQiankun.ts
import { QiankunProps } from 'vite-plugin-qiankun/dist/helper';
import { router } from '@/router/index';
export default function useQiankun(props: QiankunProps) {
props.onGlobalStateChange((state: any, prev: any) => {
// state: 变更后的状态; prev 变更前的状态
const { application, path } = state;
if (application === 'vue3' && path) {
router.push({
path,
});
}
}, true);
return {
globalValues,
};
}
3. App.vue
<template>
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
</template>
4. router.ts
import {
createRouter,
createWebHistory,
createMemoryHistory,
RouteRecordRaw,
RouterOptions,
} from 'vue-router';
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
const routes: RouteRecordRaw[] = [
// 无需 header 模块路由
{
name: 'home',
path: '/home',
component: () => import('@/views/home/index.vue'),
},
{
// 404 页面
path: '/:pathMatch(.*)*',
component: () => import('@/views/NotFound.vue'),
},
];
const router = (() => {
const config: RouterOptions = {
history: null,
routes,
};
if (qiankunWindow.__POWERED_BY_QIANKUN__)
config.history = createMemoryHistory(
import.meta.env.VITE_APP_PUBLIC_PATH || '/',
);
else
config.history = createWebHistory(
import.meta.env.VITE_APP_PUBLIC_PATH || '/',
);
return createRouter(config);
})();
export { router };
5. vite.config.ts
base: '/', // 如果为测试环境 生产环境,必须使用绝对路径: http://vue3-beta.pigsycloud.com/
server: {
headers: {
'Access-Control-Allow-Origin': '*',
},
}