🧑💻 写在开头
点赞 + 收藏 === 学会🤣🤣🤣
接入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还有手动加载微应用的方式,暂时还没有用到,可查看官方文档
本文转载于:https://juejin.cn/post/7395387245313163327
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。