记录--微前端qiankun接入vue2&vue3项目
🧑💻 写在开头
点赞 + 收藏 === 学会🤣🤣🤣
接入vue2
项目和vue3
项目
对应路由模式分别是hash
和history
主应用
主应用是基于vue3开发的一个门户网站,仅有登录以及门户列表。
基于路由配置方式
通过将微应用关联到一些 url 规则的方式,实现当浏览器 url 发生变化时,自动加载相应的微应用的功能。
-
安装
qiankun
npm i qiankun -S # 或者 yarn add qiankun
-
在主应用中注册微应用以及对应配置
新建
qiankun
文件夹,包含一个config.js
和index.js
-
config
下面存放微应用的配置信息,代码如下:
// qiankun/config.js // 这里用函数的方式,方便初始化的时候可以设置一些默认参数传进来,例如token等, // 用于下发给微应用 export const getQiankunConfig = (props = {}) => { return { subApps: [ { name: "xxx-wms", // 子应用名称,建议跟package.json一致 entry: "//192.168.1.40:8990/", // 子应用入口,本地环境下指定端口 container: "#sub-container", // 挂载子应用的dom activeRule: "/xxx/wms", // 路由匹配规则 props // 主应用与子应用通信传值 }, { name: "xxx-report", // 子应用名称,跟package.json一致 entry: "//192.168.1.40:9527/#/", // 子应用入口,本地环境下指定端口 container: "#sub-container", // 挂载子应用的dom activeRule: "/xxx/report", // 路由匹配规则 props // 主应用与子应用通信传值 }, ] }; };
index
放初始化函数以及一些其他需要配置和操作
// qiankun/index.js import { registerMicroApps } from "qiankun"; import { getQiankunConfig } from "./configs"; import { getToken } from "@/plugins/cache"; import useUserStore from "@/store/modules/user"; // 设置初始需要传递的值 export const getState = () => { const { userInfo } = useUserStore(); const state = { token: getToken(), accessUser: { loginName: userInfo.userName || "", realName: userInfo.userDisplayName || "" } }; return state; }; const { subApps } = getConfig(getState()); // 暴露注册函数,里面对应存放生命周期的钩子,可以做需要的处理 export function registerApps() { try { registerMicroApps(subApps, { beforeLoad: [ app => { console.log("before load", app); } ], beforeMount: [ app => { console.log("before mount", app); } ], afterUnmount: [ app => { console.log("before unmount", app); } ] }); } catch (err) { console.log(err); } }
新增容器,以及在路由表上增加对应路径的配置
官方原话:当微应用信息注册完之后,一旦浏览器的
url
发生变化,便会自动触发qiankun
的匹配逻辑,所有activeRule
规则匹配上的微应用就会被插入到指定的container
中,同时依次调用微应用暴露出的生命周期钩子。
- 新建
subContainer
组件,代码如下:
// subContainer.vue <template> <div id="sub-container"></div> </template> <script setup> import { start } from "qiankun"; import { registerApps } from "@/qiankun"; onMounted(()=>{ if (!window.qiankunStarted) { window.qiankunStarted = true; registerApps(); start({ prefetch: false, // 是否开启预加载, 默认为 true // sandbox: { // strictStyleIsolation: true // 开启严格的样式隔离模式 // experimentalStyleIsolation: true // 样式隔离 // } }); } }) </script>
- 路由表配置如下:
// router/xxx.js export const routes = [ // ... { // history模式需要通配所有路由, // 注意:此处xxx需要匹配上面的微前端配置里的activeRule字段 path: "/xxx/:pathMatch(.*)*", name: "xxxxx", meta: {}, component: () => import("@/views/subContainer") }, ]
微应用
-
vue3 + vite
,路由模式为history
-
新建
qiankun
相关配置文件- 安装
vite-plugin-qiankun
插件(qiankun
目前没有支持vite
的文档)
npm i vite-plugin-qiankun -D
- 在
vite.config.js
中配置插件
- 安装
// vite.config.js import qiankun from "vite-plugin-qiankun"; export default defineConfig() => { const { VITE_POWERED_BY_QIANKUN, VITE_PUBLIC_PATH } = env; return { base: VITE_PUBLIC_PATH, // 绝对路径,线上会有跨域问题 plugins: [ // VITE_POWERED_BY_QIANKUN qiankun的路径前缀 qiankun(VITE_POWERED_BY_QIANKUN, { useDevMode: true }) ] } }
- 导出对应的生命周期钩子,以及初始化方式
// qiankun.js import { renderWithQiankun, qiankunWindow } from "vite-plugin-qiankun/dist/helper"; export const render = (app, container) => { app.mount(container ? container.querySelector("#app") : "#app"); }; export const initQianKun = (app) => { renderWithQiankun({ mount(props) { const { container } = props; render(app, container); }, bootstrap() { console.log("bootstrap"); }, unmount() { app.unmount(); } }); }; // 初始化,判断是否在子应用的环境中 export const init = (app) => { qiankunWindow.__POWERED_BY_QIANKUN__ ? initQianKun(app) : render(app); };
- 路由设置,如果是
qiankun
子应用的环境,增加对应的前缀
// router/index.js import { createWebHistory, createRouter } from "vue-router"; const VITE_POWERED_BY_QIANKUN = import.meta.env.VITE_POWERED_BY_QIANKUN; const router = createRouter({ history: createWebHistory( qiankunWindow.__POWERED_BY_QIANKUN__ ? `/xxx/${VITE_POWERED_BY_QIANKUN}/` : "/" ), routes: constantRoutes, scrollBehavior(to, from, savedPosition) { if (savedPosition) { return savedPosition; } return { top: 0 }; } }); export default router;
- 初始化应用
// main.js import { createApp } from "vue"; import { init } from "@/qiankun.js"; import App from "./App"; if (window.__POWERED_BY_QIANKUN__) { // eslint-disable-next-line no-undef __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; } const app = createApp(App); init(app);
vue2 + webpack
,路由模式为 hash
-
新建
qiankun
相关配置文件- 配置
webpack
- 配置
// vue.config.js const { name } = require('./package'); module.exports = { // 开发跨域配置 devServer: { headers: { 'Access-Control-Allow-Origin': '*', }, }, configureWebpack: { output: { library: `${name}-[name]`, libraryTarget: 'umd', // 把微应用打包成 umd 库格式 jsonpFunction: `webpackJsonp_${name}`, // webpack 5 需要把 jsonpFunction 替换成 chunkLoadingGlobal }, }, };
- 导出对应的生命周期钩子,以及初始化方式
// qiankun.js import App from './App' import router from './router' import store from './store' import { setToken, setAccessUser, } from "@/utils/auth"; let instance = null export function render(props = {}) { const { container } = props instance = new Vue({ router, store, render: h => h(App) }).$mount(container ? container.querySelector('#app') : '#app') } export async function bootstrap(props) { console.log('[vue] vue app bootstraped', props) } export async function mount(props, ...args) { console.log('[vue] props from main framework', props, args); setData(props) props.onGlobalStateChange((state, prev) => { // state: 变更后的状态; prev 变更前的状态 console.log("🚀 ~ props.onGlobalStateChange ~ state:", state) setData(state) }); render(props) } export async function unmount() { instance.$destroy() instance.$el.innerHTML = '' instance = null } // 拿到主应用传过来的数据进行处理 function setData(state){ if(state['token']){ setToken(state.token) } if(state['accessUser']) { setAccessUser(state.accessUser) } } export default render
-
路由配置
// router/index.js import Router from 'vue-router' export const constantRouterMap = [ ... ] export default new Router({ scrollBehavior: () => ({ y: 0 }), base: window.__POWERED_BY_QIANKUN__ ? '/xxx/report/' : '/', routes: constantRouterMap })
- 初始化应用
import render from "./qiankun"; // 导出所有的钩子 export * from "./qiankun"; if (window.__POWERED_BY_QIANKUN__) { __webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__; }else { // 独立运行时 render(); }
总结
- 部署上线需要注意资源路径的问题
- 不同服务器下的项目需要处理跨域问题
qiankun
还有手动加载微应用的方式,暂时还没有用到,可查看官方文档