qiankun + Vue + React18 搭建微前端
本章节基于 Vue + qianakun 的方式搭建的微前端,微前端有主应用与微应用构成。
主应用: Vue3 + Vite
微应用:
- Vue2 + Webpack
- Vue3 + Vite
- React18 + webpack
- React18 + vite
主应用配置:
-
主应用需安装 qiankun 微前端架构 (本章节使用的是最新版本:2.10.16)
pnpm add qiankun 或者 # yarn add qiankun # 或者 npm i qiankun -S -
注册微应用:在
src目录新增qiankun/index.tsimport {registerMicroApps, start, addGlobalUncaughtErrorHandler} from 'qiankun' import type {RegistrableApp, FrameworkLifeCycles} from 'qiankun/lib' // 微应用注册信息 const apps: RegistrableApp<object>[] = [ { name: 'sonVue2', // 微应用名称 entry: '//localhost:3002/', // 微应用入口 container: '#container', // 微应用挂载节点 activeRule: '/son-vue2', // 微应用路由 }, { name: 'sonVue3', entry: '//localhost:3003/', container: '#container', activeRule: '/son-vue3', }, { name: 'sonReact1', entry: '//localhost:3004/', container: '#container', activeRule: '/son-react1', }, { name: 'sonReact2', entry: '//localhost:3005/', container: '#container', activeRule: '/son-react2', } ] // 全局的微应用生命周期钩子 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.tsimport {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() } } const actions = new Actions() export default actions -
vue 路由配置
import {createRouter, createWebHistory} from 'vue-router' const router = createRouter({ routes: [...], // 路由模式配置为history模式 history: createWebHistory(import.meta.env.BASE_URL) }) // 处理微应用内部路由跳转后无法切换到主应用或其他微应用问题 router.afterEach((to) => { 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 + Webpack
微应用无需安装额外的依赖。
-
在
src目录新增qiankun/public-path.jsif (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: '/micro-app-vue2/', configureWebpack: { output: { library: `sonVue2`, // 微应用名称 libraryTarget: 'umd', // 打包成 umd 库格式 chunkLoadingGlobal: `webpackJsonp_sonVue2`, }, }, }) -
封装 qiankun 全局通信方法(可选):在
src目录新增qiankun/actions.jsclass Actions { actions = { onGlobalStateChange: null, setGlobalState: null, } initAction(actions) { this.actions = actions } onGlobalStateChange() { return this.actions.onGlobalStateChange(...arguments) } setGlobalState() { return this.actions.setGlobalState(...arguments) } } const actions = new Actions() export default actions -
vue 路由配置
import Vue from 'vue' import VueRouter from 'vue-router' Vue.use(VueRouter) const router = new VueRouter({ routes:[...], base: window.__POWERED_BY_QIANKUN__ ? '/son-vue2' : process.env.BASE_URL', // 配置微应用访问路径 mode: 'history', // 路由模式配置为history模式 }) export default router -
在
main.js中应用import '@/qiankun/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() {} // 挂载 export async function mount(props) { actions.initAction(props) // 初始化qiankun通信 render(props); } // 更新 export async function update(){} // 卸载 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: '/micro-app-vue3/', server: { port: 3003, host: '0.0.0.0', cors: true, // 允许本地跨域请求 }, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } } }) -
封装 qiankun 全局通信方法(可选):在
src目录新增qiankun/actions.jsclass Actions { actions = { onGlobalStateChange: null, setGlobalState: null, } initAction(actions) { this.actions = actions } onGlobalStateChange() { return this.actions.onGlobalStateChange(...arguments) } setGlobalState() { return this.actions.setGlobalState(...arguments) } } const actions = new Actions() export default 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__ ? '/son-vue3' : import.meta.env.BASE_URL), }) 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('#app') : '#app'); } // 如果不是在 qiankun 环境中运行,则独立启动 if (!qiankunWindow.__POWERED_BY_QIANKUN__) { render() } // 导出微应用生命周期钩子 renderWithQiankun({ // 初始化 function bootstrap() {} // 挂载 function mount(props) { actions.initAction(props) // 初始化qiankun通信 render(props) } // 更新 update: () => {}, // 卸载 function unmount(props) { app.unmount(); } })
React18 + Webpack
微应用无需安装额外的依赖。
-
在
src目录新增qiankun/public-path.jsif (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } -
需要修改webpack需要安装@craco/craco 依赖
pnpm add @craco/craco -D 或者 # yarn add @craco/craco -D # 或者 npm i @craco/craco -D修改
craco.config.js配置const path = require('path') module.exports = { devServer: { port: 3004, hot: true, host: '0.0.0.0', headers: { 'Access-Control-Allow-Origin': '*' }, }, webpack: { configure: (webpackConfig, { paths }) => { webpackConfig.output = { ...webpackConfig.output, path: path.resolve(__dirname, 'micro-app-react1'), library: 'sonReact1', libraryTarget: 'umd', chunkLoadingGlobal: 'webpackJsonp_sonReact1', globalObject: 'window', } // 返回修改后的配置 return webpackConfig }, alias:{ "@": path.resolve(__dirname, './src') } }, } -
封装 qiankun 全局通信方法(可选):在
src目录新增qiankun/actions.jsclass Actions { actions = {} initAction (actions) { this.actions = actions } onGlobalStateChange () { return this.actions.onGlobalStateChange(...arguments) } setGlobalState () { return this.actions.setGlobalState(...arguments) } } const actions = new Actions() export default actions -
react 路由配置
import { createBrowserRouter } from 'react-router-dom' import { lazy, Suspense } from 'react' import isQiankun from '@/qiankun/isQiankun' const router = createBrowserRouter( [...], { basename: window.__POWERED_BY_QIANKUN__ ? '/son-react1' : '/micro-app-react1' } ) export default router -
在
index.js中应用import { createRoot } from 'react-dom/client' import './index.scss' import "@/qiankun/public-path" import { RouterProvider } from 'react-router-dom' import router from '@/router' import actions from '@/qiankun/actions' let root = null function render (props = {}) { const { container } = props root = createRoot(container ? container.querySelector('#app') : document.querySelector('#app')) root.render(<RouterProvider router={router}/>) } // 独立运行时 if (!window.__POWERED_BY_QIANKUN__) { render({}) } // 导出微应用生命周期钩子 // 初始化 export async function bootstrap () {} // 挂载 export async function mount (props) { actions.initAction(props) // 初始化qiankun通信 render(props) } // 更新 export async function update () {} // 卸载 export async function unmount () { root.unmount() root = null }
React18 + Vite
由于是 Vite 应用需要安装 Vite 版本的 qiankun 依赖
pnpm add vite-plugin-qiankun -D 或者 # yarn add vite-plugin-qiankun -D # 或者 npm i vite-plugin-qiankun -D
-
修改
vite.config.ts配置import {fileURLToPath, URL} from 'node:url' import {defineConfig} from 'vite' import react from '@vitejs/plugin-react' import qiankun from 'vite-plugin-qiankun' export default defineConfig({ plugins: [ react(), qiankun('sonReact2', { useDevMode: true }) ], base: '/micro-app-react2/', server: { port: 3005, host: '0.0.0.0', cors: true, hmr: false, // 在本地开发测试时,需要关闭热更新 }, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)) } } }) -
封装 qiankun 全局通信方法(可选):在
src目录新增qiankun/actions.tsimport {QiankunProps} from "vite-plugin-qiankun/es/helper"; type OnGlobalStateChangeCallback = (state: Record<string, any>, prevState: Record<string, any>) => void class Actions { actions: QiankunProps | null = null initAction(actions: QiankunProps) { this.actions = actions } onGlobalStateChange(callback: OnGlobalStateChangeCallback, fireImmediately?: boolean) { return this.actions?.onGlobalStateChange(callback, fireImmediately) } setGlobalState(state: Record<string, any>): boolean { return this.actions?.setGlobalState(state) } } const actions = new Actions() export default actions -
react 路由配置
import {createBrowserRouter} from "react-router-dom"; import {qiankunWindow} from 'vite-plugin-qiankun/dist/helper' const router = createBrowserRouter( [...], { basename: qiankunWindow.__POWERED_BY_QIANKUN__ ? "/son-react2" : import.meta.env.BASE_URL } ) export default router -
在
main.ts中应用import {createRoot} from 'react-dom/client' import './index.scss' import {RouterProvider} from "react-router-dom"; import router from "@/router"; import {qiankunWindow} from 'vite-plugin-qiankun/dist/helper' import {renderWithQiankun} from "vite-plugin-qiankun/dist/helper"; import actions from "@/qiankun/actions.ts"; // 引用qiankun通信 type propsType = { container?: ReactDOM.Container, } let root: ReturnType<typeof createRoot> | null = null function render(props: propsType = {}) { const {container} = props; root = createRoot(container ? container.querySelector("#app")! : document.querySelector("#app")!) root.render( <RouterProvider router={router}/> ) } // 如果不是在 qiankun 环境中运行,则独立启动 if (!qiankunWindow.__POWERED_BY_QIANKUN__) { render({}) } // 导出微应用生命周期钩子 renderWithQiankun({ // 初始化 bootstrap: () => {}, // 挂载 mount: (props) => { // 初始化qiankun通信 actions.initAction(props) render(props) }, // 更新 update: () => {}, // 卸载 unmount: () => { root && root.unmount() root = null }, })
9160

被折叠的 条评论
为什么被折叠?



