vue3 webpack qiankun微前端
qiankun: https://qiankun.umijs.org/zh/guide
demo源码gitee:https://gitee.com/philippines-kisses-snow/qiankun-demo
官方对微应用的说明:通常这种场景下微应用是一个不带路由的可独立运行的业务组件。 微应用不宜拆分过细,建议按照业务域来做拆分。业务关联紧密的功能单元应该做成一个微应用,反之关联不紧密的可以考虑拆分成多个微应用。 一个判断业务关联是否紧密的标准:看这个微应用与其他微应用是否有频繁的通信需求。如果有可能说明这两个微应用本身就是服务于同一个业务场景,合并成一个微应用可能会更合适。
准备:
主应用(qiankun-main)
子应用(qiankun_child1)
vue create qiankun-main
vue create qiankun_child1
一、主应用(qiankun-main)
安装包
npm i vue-router
npm i qiankun -S
添加路由
//router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
name: 'Home',
component: () => import('../views/Home.vue'),
},
{
path: '/:catchAll(.*)*',
name: 'error',
meta: {
name: '404',
},
component: () => import('../views/404.vue'),
},
]
})
export default router
//views/Home.vue
<template>
<div>HOME</div>
<div>
<button @click="toChild1">应用1</button>
<button @click="toChild2">应用2</button>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
const router = useRouter()
function toChild1() {
// 切换到子应用的/demo路由页面
router.push('/child1/demo')
}
function toChild2() {
// 切换到子应用的/page1路由页面
router.push('/child2/page1')
}
</script>
// 404.vue
<template>
<div>404</div>
</template>
//App.vue
<template>
<div>
<router-view></router-view>
</div>
</template>
配置main.ts
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { addGlobalUncaughtErrorHandler, registerMicroApps, start } from 'qiankun';
import router from './router'
createApp(App).use(router).mount('#app')
registerMicroApps([]);
// 全局异常捕获(可省去)
addGlobalUncaughtErrorHandler((event: any) => {
if(typeof event !== 'string') {
const mircoAppName = event?.error?.appOrParcelName
if(mircoAppName && event.type === 'error') {
console.error(`${mircoAppName}应用加载失败`)
}
}
})
start();
二、子应用(qiakun_child1)
webpack配置
// vue.config.js
const { defineConfig } = require('@vue/cli-service')
const { name } = require('./package.json');
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 8085,
headers: {
'Access-Control-Allow-Origin': '*', // 设置允许跨域请求,否则会因为在其他端口号获取资源报错
},
},
configureWebpack: {
output: {
library: `${name}-[name]`,
libraryTarget: 'umd',
// webpack 4 将chunkLoadingGlobal改为jsonpFunction
chunkLoadingGlobal: `webpackJsonp_` + name,
},
},
})
main.js配置
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from './router'
if ((window as any).__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = (window as any).__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
let router: any
let app: any
function render({ container } = {} as any) {
router = createRouter({
history: createWebHistory((window as any).__POWERED_BY_QIANKUN__ ? '/child2' : '/'),
routes,
})
app = createApp(App)
app.use(router).mount(container ? container.querySelector('#app') : '#app')
}
if(!(window as any).__POWERED_BY_QIANKUN__) {
render()
}
// 导出第一次进入当前子应用的钩子函数
export async function bootstrap() {
}
// 导出每次创建挂载时的钩子函数
export async function mount(props: any) {
render(props)
}
// 导出每次销毁时的钩子函数
export async function unmount(props: any) {
app.unmount()
app._container.innerHTML = ''
app = null
router = null
}
路由配置
// router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
import Demo from '../components/Demo.vue'
import HelloWorld from '../components/HelloWorld.vue'
export const routes: RouteRecordRaw[] = [
{
path: '/demo',
component: Demo
},
{
path: '/hello',
component: HelloWorld
}
]
三、将子应用添加到主应用
// 在主应用的 main.ts 的 registerMicroApps 当中加入子应用路由地址
registerMicroApps([
{
name: 'qiankun_child1',
entry: 'http://xxx.xxx.xxx.xxx:xxx', // 子应用的访问地址
container: '#child1',
activeRule: 'child1',
},
]);
// 在主应用中添加子应用路由页面,主应用router/index.ts的router中加入:
{
path: '/child1/:pathMatch(.*)',
name: 'child1',
component: () => import('../views/app1.vue'),
}
// app1.vue
<template>
<div id="child1"></div>
</template>
<script setup lang="ts">
import { onMounted } from 'vue'
import { start } from 'qiankun';
onMounted(() => {
start({
prefetch: false,
sandbox: {
experimentalStyleIsolation: true,
}
});
})
</script>