项目背景:基于已经上线的几个公司内部系统,要做一个入口来统一登录、查看数据等信息。可选的框架有iframe、qiankun,由于iframe各种问题,最终选择利用qiankun来整合他们。
应用结构预览
主应用
npm i qiankun
- 配置main.ts
...vue,pinia基础配置
// 主要配置
import { registerMicroApps, start, addGlobalUncaughtErrorHandler} from 'qiankun'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')
// 挂载应用后,注册子应用
const dev = import.meta.env.MODE === 'development'
// 微应用name、url应该放数据库,从接口获取,避免修改端口号后重新部署。
const microApps = [
{
name: 'microAppName',
entry: dev ? '//localhost:9090' : 'https://......',
container: '#container',
activeRule: '/micro/microAppName'
},
...
]
// 注册所有微应用
registerMicroApps(microApps)
// 可以加个全局错误捕获,如果配置正确,可以不加
addGlobalUncaughtErrorHandler((event) => console.warn('qiankun global error',event));
// 不要start 不要start 不要start,否则会报错:容器不存在
// start()
- 定义微应用容器
<script setup lang="ts">
import { RouterLink, RouterView, useRouter } from 'vue-router'
import { onMounted } from 'vue'
const router = useRouter()
function toApp(path) {
// 网上写法
// history.pushState({}, '',path)
// push也可以
router.push(path)
}
onMounted(() => {
if(!window.qiankunStarted) {
window.qiankunStarted = true
nextTick(() => {
start()
})
}
})
</script>
<template>
<a @click="toApp('/xxx')">进入子应用1</a><br/>
<a @click="toApp('/ccc')">进入子应用2</a>
<div id="projects-container"></div>
</template>
微应用
npm i vite-plugin-qiankun
- 配置插件
// vite.config.ts
import qiankun from 'vite-plugin-qiankun'
export default ({mode}) =>{
return defineConfig({
plugins: [
...,
qiankun('microAppName', {
useDevMode: true // vite写法,webpack写法请查看对应文档
})
],
server: {
...,
origin: 获取此应用的资源地址,为了解决子应用资源404问题(如果不加,微应用获取资源的baseurl是主应用,所以会404)
}
})
}
- 配置主文件
// main.ts
import {
renderWithQiankun,
qiankunWindow,
} from 'vite-plugin-qiankun/dist/helper'
if (!qiankunWindow.__POWERED_BY_QIANKUN__) {
createApp(App)
.use(createPinia())
.use(router)
.mount("#app")
} else {
// 配置微应用。给qiankun定义生命周期
const container = document.getElementById('app')
if (container) {
renderWithQiankun({
mount(props) {
app = createApp(App)
app
.use(createPinia())
.use(router)
.mount(
props.container
? props.container.querySelector('#app')
: document.getElementById('app')
)
// 监听主应用注入的全局状态
props.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
});
},
bootstrap() {},
update() {},
unmount() {
app?.unmount()
},
})
} else {
console.log('not exist');
}
}
- 配置路由
需要将微应用的路由模式改成hash模式,否则资源获取失败
import { createRouter, createWebHashHistory } from 'vue-router'
import { qiankunWindow } from "vite-plugin-qiankun/dist/helper";
const routes = [...]
const router = createRouter({
history: createWebHashHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? "/microAppName" : '/'),
routes
})
坑点
- 微应用请求资源报404 --> 微应用配置插件部分
- 刷新页面后,提示容器不存在
主应用main文件不要start(),在容器组件mounted周期内调用start() -->配置主应用的定义微应用容器部分
- 微应用样式错乱
要么就配置qiankun样式完全隔离,要么就统一主、微应用的ui框架版本(要保证主微应用ui一样,就采用第二种方式)
- 刷新页面,菜单高亮失效
因为刷新浏览器导致子任务容器重新挂载,微应用无法获取当前路由信息,重新匹配跟路由。
解决方案:微应用中点击菜单,做数据持久化,刷新页面后先从 sessionStorage 中取。
- 微应用请求接口报404
请求的域名指向了主应用导致无法获取接口地址,在主应用拦截后,转发给子应用
// 主应用vite.config.ts
proxy: {
'/microApi': {
target: "微应用的地址",
changeOrigin: true,
secure: false,
rewrite: path => path.replace(/^\/microApi/, '')
},
}
// 微应用 axios
import axios from 'axios'
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper'
const instance = axios.create({
baseURL: qiankunWindow.__POWERED_BY_QIANKUN__ ? '/microApi' : '',
timeout: 5000,
})
- 不知道哪里写的不对还是projects被当做关键字,微应用上级路由不要写/projects,否则会报404
- 还有404问题,让后端配置Nginx