介绍
qiankun 是一个基于 single-spa 的微前端实现库,旨在帮助大家能更简单、无痛的构建一个生产可用微前端架构系统。
公司有一个门户网站需要嵌入其他系统,使用iframe会有很多的兼容性问题,如果使用qiankun框架,不仅可以绕过这些iframe的坑,还可以进行很多的“骚操作”。
快速上手
qiankun官网的快速上手很简单,不是因为他们懒得写,而是引入qiankun的确是非常简单。
只需要在主应用中引入qiankun,微服务做相应的配置,就可以,下面就介绍我在项目中如何引入。
主应用
-
安装
npm install -S qiankun
-
在main.js入口文件
import { registerMicroApps, start } from 'qiankun'; const getActiveRule = (hash) => (location) => location.hash.startsWith(hash); registerMicroApps([ { name: 'TPP', entry: 'http://localhost:7000/', // 主应用挂载的节点 container: '#TPP', // 主应用使用的hash模式 activeRule: getActiveRule('#/TPP'), }, { name: 'PTP', entry: 'http://localhost:7001/', // 主应用挂载的节点 container: '#PTP', // 主应用使用的hash模式 activeRule: getActiveRule('#/PTP'), }, ]); start();
微服务配置( vue/cli3创建应用)
仅针对开发环境
- 微服务入口文件
qiankun会在微应用注入一个全局变量,用于针对此变量来做一些配置
if (window.__POWERED_BY_QIANKUN__) {
// 如果是正常访问,则为undefined,如果是qiankun访问,则为true
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
function render(props = {}) {
/**
props 参数
container: 主应用挂载微应用的节点
mountParcel: ƒ ()
name: "TPP" 微应用name
onGlobalStateChange: ƒ onGlobalStateChange(callback, fireImmediately)
setGlobalState: ƒ setGlobalState()
singleSpa
*/
const { container } = props;
const mountPath = container ? container.querySelector('#TPPAPP') : '#TPPAPP';
window.sfopenpc = new Vue({
render: (h) => h(App),
store,
router,
}).$mount(mountPath);
}
// 如果正常访问,则传入空对象,用于正常的配置
if (!window.__POWERED_BY_QIANKUN__) {
render({});
}
// qiankun的生命周期回调
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}
export async function mount(props) {
console.log('[vue] props from main framework', props);
// 如果qiankun框架引入,则会在页码加载完成之后调用此方法
render(props);
}
export async function unmount() {
window.sfopenpc.$destroy();
window.sfopenpc.$el.innerHTML = '';
window.sfopenpc = null;
// router = null;
}
- webpack配置(开发环境)
由于是不同端口,所以会出现跨域的问题,所以需要在代理服务器进行配置headers,支持微服务的Access-Control-Allow-Origin
const { name } = require('./package.json');
module.exports = {
configureWebpack: {
devServer: {
headers: {
// 本地服务器接受跨域
'Access-Control-Allow-Origin': '*',
},
},
},
output: {
library: `${name}-[name]`,
libraryTarget: 'umd', // 把微应用打包成 umd 库格式,不配置的话,主应用导入会报错
jsonpFunction: `webpackJsonp_${name}`,
},
}
效果
点击上面的菜单栏进行跳转
已经成功引入。
其他问题
主应用与微应用跳转问题
如果主应用和微应用同时使用hash
模式的话,如果不做特殊处理的话,就会出现跳转异常。
我这里简单写了一个简单的中间件,原理是在微应用的入口文件新增一个前缀处理,使得如果是qiankun框架访问的时候,自动添加路由跳转前缀的同时,点击时也会自动重定向对应的前缀。
微应用配置
- 中间件QiankunRouter
import Router from 'vue-router';
function handleRouter(router, prefix = '') {
if (router.path) {
if (router.path === '/') {
router.path = `${prefix}`;
} else {
router.path = `${prefix}${router.path}`;
}
}
if (router.children && router.children.length) {
for (const r of router.children) {
handleRouter(r, prefix);
}
}
}
// eslint-disable-next-line no-unused-vars
function decorate(router, { isQiankun, prefix }) {
if (isQiankun) {
for (const r of router) {
handleRouter(r, prefix);
}
}
return router;
}
export default class QiankunRouter extends Router {
constructor(props) {
decorate(props.routes, {
isQiankun: props.isQiankun,
prefix: props.prefix,
});
super(props);
this.isQiankun = !!props.isQiankun;
this.prefix = props.prefix || '';
if (this.isQiankun) {
// 注册跳转路由前置,跳转时自动新增前缀
this.qiankunbeForeEach();
}
}
// eslint-disable-next-line no-unused-vars
qiankunbeForeEach() {
super.beforeEach((to, from, next) => {
if (this.isQiankun && !to.path.includes('/microApp')) {
if (to.path === '/') {
next({
path: this.prefix,
});
} else {
next({
path: `${this.prefix}${to.path}`,
});
}
}
next();
});
}
}
- 使用QiankunRouter替换Vue-router
/* eslint-disable no-unused-vars */
import Vue from 'vue';
// import router from 'Vue-router'
import QiankunRouter from '../qiankun/decorateRouter';
// 路由信息
const routes = [{
path:'/',
component: () => import('./qiankun/index')
}]
Vue.use(QiankunRouter);
const router = new QiankunRouter({
mode: 'hash',
routes: vuexHoc(routes),
// eslint-disable-next-line no-underscore-dangle
isQiankun: !!window.__POWERED_BY_QIANKUN__,
// 路由跳转前缀
prefix: '/microApp/TPP',
});
export default router;
主应用配置修改
只需要修改路由信息和qiankun主路由检测路径即可
- qiankun主路由检测路径
const qiankunConfig = [{
name: 'TPP',
// 访问微应用的跳转首页路径 新增#/microApp/TPP,匹配的是原来的/
entry: 'http://localhost:7001/#/microApp/TPP',
container: '#microApp',
// 主应用hash配置前缀
activeRule: getActiveRule('#/microApp/TPP'),
}]
- 路由信息
{
// 给微应用配置一个固定前缀
path: '/microApp/*',
name: '微应用',
component: () => import('../views/microApp.vue'),
},
微应用的弹出新窗口
弹出新窗口是可以使用相对路径,浏览器会自动获取当前的路由信息,但是如果使用主应用打开的时候打开新窗口就会异常了。
解决方案有如下:
- 封装一个跳转窗口的方法,统一使用该方法进行跳转
- 如果使用的是<a>标签跳转新窗口的话,就不能使用相应路径,而改用URL的绝对路径替换。
js文件的预加载问题
在开发环境中,跳转路由的时候,会出现一个明显的“白屏时间”,这可能是因为请求多次导致加载时间变长,目前暂时还没有确定的解决方案,可能需要在主应用中加一个loading优化显示。