Vue + qiankun 搭建微前端
本章节基于 Vue + qianakun 的方式搭建的微前端,微前端有主应用与微应用构成。
**主应用:**Vue3 + Vite
微应用:
- Vue2 + Webpack5
- Vue3 + Vite
主应用配置:
-
主应用需安装 qiankun 微前端架构 (本章节使用的是最新版本:2.10.16)
pnpm add qiankun 或者 # yarn add qiankun # 或者 npm i qiankun -S
-
注册微应用:在
src
目录新增qiankun/index.ts
import {registerMicroApps, start, addGlobalUncaughtErrorHandler} from ‘qiankun’
import type {RegistrableApp, FrameworkLifeCycles} from ‘qiankun/lib’
// 微应用注册信息
const apps: RegistrableApp<object>[] = [
//
{
name: 'sonVue2', // 微应用名称
entry: '//localhost:3002/child/son-vue2', // 微应用入口
container: '#container', // 微应用挂载节点
activeRule: '/son-vue2', // 微应用路由
},
{
name: 'sonVue3', // 微应用名称
entry: '//localhost:3003/', // 微应用入口
container: '#container', // 微应用挂载节点
activeRule: '/son-vue3', // 微应用路由
}
]
// 全局的微应用生命周期钩子
const lifecycle: FrameworkLifeCycles<Object> = {
beforeLoad: async (app) => {
console.log(app)
},
beforeMount: async (app) => {
console.log(app)
},
afterMount: async (app) => {
console.log(app)
},
beforeUnmount: async (app) => {
console.log(app)
},
afterUnmount: async (app) => {
console.log(app)
}
}
registerMicroApps(apps, lifecycle)
// 添加全局的未捕获异常处理器
addGlobalUncaughtErrorHandler((event: any) => {
const {message: msg} = event
if (msg && msg.includes('died in status LOADING_SOURCE_CODE')) {
console.error('微应用加载失败')
} else {
console.error(event)
}
})
export default start
```
-
封装 qiankun 全局通信方法(可选):在
src
目录新增qiankun/actions.ts
import {initGlobalState} from 'qiankun' import type {MicroAppStateActions, OnGlobalStateChangeCallback} from 'qiankun/lib' class Actions { declare actions: MicroAppStateActions // 设置全局状态 setAction(state: Record<string, any>): void { this.actions = initGlobalState(state) } // 监听全局状态 onGlobalStateChange(callback: OnGlobalStateChangeCallback, fireImmediately?: boolean): void { return this.actions.onGlobalStateChange(callback, fireImmediately) } // 修改全局状态 setGlobalState(state: Record<string, any>): boolean { return this.actions.setGlobalState(state) } // 移除当前应用的状态监听 offGlobalStateChange(): boolean { return this.actions.offGlobalStateChange() } } export default new Actions()
-
vue 路由配置
// router/index import {createRouter, createWebHistory} from 'vue-router' const router = createRouter({ routes: [...], // 路由模式配置为history模式 history: createWebHistory(import.meta.env.BASE_URL) }) // 处理微应用内部路由跳转后无法切换到主应用或其他微应用问题 router.afterEach((to, from) => { const current = router.options.history.state.current Object.assign(router.options.history.state, { current: current === '/' ? '/' : to.fullPath }) }) export default router
-
在
main.ts
中应用import '@/assets/main.scss' import {createApp} from 'vue' import App from './App.vue' import router from './router' import {createPinia} from "pinia"; import {useMessageStore} from "@/stores/message"; import start from '@/qiankun/index' // 引用注册微应用文件 import actions from '@/qiankun/actions'; // 引用qiankun通信 const app = createApp(App) app.use(router) app.use(createPinia()) app.mount('#host-app') const store = useMessageStore() // 设置全局状态 actions.setAction({message: store.message}) // 监听全局状态 actions.onGlobalStateChange((state, prev) => { store.setMessage(state.message) }) // 启动qiankun start({ prefetch: true, // 开启预加载 sandbox: true // 开启沙箱 })
-
修改
App.vue
<script setup lang="ts"> ... </script> <template> ... <!-- 微应用挂载节点 --> <div id="container"></div> </template> <style scoped lang="scss"> ... </style>
微应用搭建:
Vue2 + Webpack5
微应用无需安装额外的依赖。
-
在
src
目录新增public-path.js
if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; }
-
修改
vue.config.js
配置const {defineConfig} = require('@vue/cli-service') module.exports = defineConfig({ devServer: { port: 3002, host: '0.0.0.0', // 允许本地跨域请求 headers: { 'Access-Control-Allow-Origin': '*', }, }, // 配置公共访问路径 publicPath: '/child/son-vue2/', configureWebpack: { output: { library: `sonVue2`, // 微应用名称 libraryTarget: 'umd', // 打包成 umd 库格式 chunkLoadingGlobal: `webpackJsonp_sonVue2`, }, }, transpileDependencies: true, })
-
封装 qiankun 全局通信方法(可选):在
src
目录新增qiankun/actions.js
class Actions { actions = { onGlobalStateChange: null, setGlobalState: null, offGlobalStateChangeL: null } // 初始化 qiankun 全局通信 initAction(actions) { this.actions = actions } // 监听全局状态 onGlobalStateChange() { return this.actions.onGlobalStateChange(...arguments) } // 修改全局状态 setGlobalState() { return this.actions.setGlobalState(...arguments) } } export default new Actions();
-
vue 路由配置
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const router = new VueRouter({ routes:[...], base: window.__POWERED_BY_QIANKUN__ ? '/' : '/child/son-vue2', // 配置微应用访问路径 mode: 'history', // 路由模式配置为history模式 }) export default router
-
在
main.js
中应用import './public-path'; import Vue from 'vue' import App from './App.vue' import router from './router' import actions from "@/qiankun/actions"; // 引用qiankun通信 Vue.config.productionTip = false let app = null; function render(props = {}) { const {container} = props; app = new Vue({ router, render: (h) => h(App), }).$mount(container ? container.querySelector('#app') : '#app'); } // 独立运行时 if (!window.__POWERED_BY_QIANKUN__) { render(); } // 导出微应用生命周期钩子 // 初始化 export async function bootstrap() { console.log('bootstrap') } // 挂载 export async function mount(props) { actions.initAction(props) // 初始化qiankun通信 render(props); } // 卸载 export async function unmount() { app.$destroy(); app.$el.innerHTML = ''; app = null; }
Vue3 + Vite
由于是 Vite 应用需要安装 Vite 版本的 qiankun 依赖
pnpm add vite-plugin-qiankun -D 或者 # yarn add vite-plugin-qiankun -D # 或者 npm i vite-plugin-qiankun -D
-
修改
vite.config.js
配置import {fileURLToPath, URL} from 'node:url' import {defineConfig} from 'vite' import vue from '@vitejs/plugin-vue' import qiankun from 'vite-plugin-qiankun' export default defineConfig({ plugins: [ vue(), // 配置qiankun qiankun('sonVue3', { useDevMode: true }) ], base: '/child/son-vue3/', server: { port: 3003, host: '0.0.0.0', cors: true, // 允许本地跨域请求 }, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } } })
-
封装 qiankun 全局通信方法(可选):在
src
目录新增qiankun/actions.js
class Actions { actions = { onGlobalStateChange: null, setGlobalState: null, offGlobalStateChangeL: null } // 初始化 qiankun 全局通信 initAction(actions) { this.actions = actions } // 监听全局状态 onGlobalStateChange() { return this.actions.onGlobalStateChange(...arguments) } // 修改全局状态 setGlobalState() { return this.actions.setGlobalState(...arguments) } } export default new Actions();
-
vue 路由配置
import {createRouter, createWebHistory} from 'vue-router' import {qiankunWindow} from 'vite-plugin-qiankun/dist/helper.js' const router = createRouter({ routes: [...], // 路由模式配置为history模式,并配置微应用访问路径 history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? '/' : '/child/son-vue3/'), }) export default router
-
在
main.js
中应用import './assets/main.css' import {createApp} from 'vue' import App from './App.vue' import router from './router' import {renderWithQiankun, qiankunWindow} from 'vite-plugin-qiankun/dist/helper' import actions from "@/qiankun/actions.js"; // 引用qiankun通信 let app = null function render(props = {}) { const {container} = props; app = createApp(App) app.use(router) app.mount(container ? container.querySelector('#son-vue3') : '#son-vue3'); } // 如果不是在 qiankun 环境中运行,则独立启动 if (!qiankunWindow.__POWERED_BY_QIANKUN__) { render() } // 初始化 function bootstrap() { console.log('bootstrap') } // 挂载 function mount(props) { actions.initAction(props) // 导出微应用生命周期钩子 render(props) } // 卸载 function unmount(props) { app.unmount(); } // 导出微应用生命周期钩子 renderWithQiankun({ bootstrap, mount, unmount, })