无限极服务
146.43MB · 2025-10-24
在日常开发中,我们经常会遇到多个项目,多个代码库,不同的基础框架,想要整合进一个系统的情况。面对这种情况,我们有多种处理方式,以前最常用的是使用iframe加载。但是iframe加载在视觉上,会感觉很割裂,特别是弹窗无法真正的全屏。这时候我们可能就会考虑用微前端实现,其中最常用的就是qiankun微前端框架。 接下来,我们一起看一下一个基于vue2 webpack作为基础框架的项目如何快速改造为微前端项目,目标是把项目改为同时适合作为微前端的主应用,也适合作为微前端的子应用
安装qiankun
npm install qiankun --save-dev
VUE_APP_SUB_APPS,含子应用的项目名称name,子应用的入口页面entry。这组变量将在后续用于在主应用中注册子应用VUE_APP_SUB_APPS=[{"name":"vueApp1","entry":"http://localhost:8085"}]
VUE_APP_PROJECT_NAME,名字保持跟提供给主应用的name一致configureWebpack: {
performance: {
maxAssetSize: 2000000,
maxEntrypointSize: 2000000
},
name: process.env.VUE_APP_PROJECT_NAME,
output: {
library: process.env.VUE_APP_PROJECT_NAME,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式
chunkLoadingGlobal: `webpackJsonp_${process.env.VUE_APP_PROJECT_NAME}`
},
},
//html 页面内容容器中添加
<div id="subapp-container" ref="subappContainer"> </div>
// js 部分添加
mounted() {
if (this.$refs.subappContainer) {
this.$refs.subappContainer.appendChild(window.top.$microSubContaner);
}
},
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
import './qiankun/public-path';
let instance = null;
function render(props = {}) {
const { container } = props;
Vue.use(permission);
Vue.use(VueClipboard);
instance = new Vue({
router,
store,
i18n,
render: (h) => h(App)
}).$mount(container ? container.querySelector('#app') : '#app');
}
// 提前创建微应用挂载点,以免微应用挂载时节点不存在
const $div = document.createElement('div');
$div.classList.add('sub-container');
$div.style.width = '100%';
$div.style.height = '100%';
window.$microSubContaner = $div;
// 作为主应用或者独立应用时执行
if (!window.__POWERED_BY_QIANKUN__) {
render();
initMain();
// 等待子应用注册完成后再启动
document.addEventListener('DOMContentLoaded', initMainStart);
}
// 作为子应用使用时
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}
export async function mount(props) {
console.log('[vue] props from main framework', props);
render(props);
// 接收主应用传递的参数,以及给主应用传递参数
props.onGlobalStateChange((state, prev) => {
// state: 变更后的状态; prev 变更前的状态
console.log(state, prev);
});
props.setGlobalState({});
}
export async function unmount() {
// 实例一定要注销
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
}
main.vue使用的initMain函数相关代码
import { registerMicroApps, start, setDefaultMountApp, initGlobalState} from 'qiankun';
const getActiveRule = (hash) => (location) => location.hash.startsWith(hash);
const getSubApps = () => {
let subApps = JSON.parse(process.env.VUE_APP_SUB_APPS || '[]');
let subAppsList = [];
for (let i = 0; i < subApps.length; i++) {
subAppsList.push({
name: subApps[i].name,
entry: subApps[i].entry,
container: window.top.$microSubContaner,
activeRule: getActiveRule(`#/${subApps[i].name}`)
});
}
return subAppsList;
};
export function initMain() {
registerMicroApps(getSubApps());
// 接收子应用传递的数据,以及给子应用传递数据
const { onGlobalStateChange } = initGlobalState({ aa: 1 });
onGlobalStateChange((state) => {
console.log(state, 'state');
});
}
export function initMainStart() {
setDefaultMountApp('/');
start();
}
// 当为子应用时添加路由前缀
let router = new VueRouter({
base: window.__POWERED_BY_QIANKUN__
? `/${process.env.VUE_APP_PROJECT_NAME}`
: '/',
routes,
// mode: 'history',
scrollBehavior() {
return { y: 0 };
}
});
const subApps = process.env.VUE_APP_SUB_APPS;
export const isInQiankun = !!window.__POWERED_BY_QIANKUN__;
export const subAppsNames =subApps.map(item => item.name);
routes.js 修改根据菜单数据的动态路由的根路由
//SubLayoutIndex 和Layout的区别是SubLayoutIndex不含菜单和系统顶部公共部分,作为子应用时不应有这些
return {
path: LAYOUT_PATH,
component: isInQiankun ? SubLayoutIndex : Layout,
redirect: HOME_PATH ?? homePath,
children: routes
};
这样就完成了一个项目的微前端改造。