1、主项目配置
1.1 micro.vue 组件
<template><div id="container-sub-app"></div>
</template><script>
import { loadMicroApp } from 'qiankun';
import actions from '@/utils/actions.js';export default {name: 'microApp',mixins: [actions],data() {return {microApp: null};},mounted() {const getMicroInfo = this.getMicroInfo();this.microApp = loadMicroApp(getMicroInfo, {singular: true});},beforeDestroy() {console.log('beforeDestroy...');this.microApp.unmount();},methods: {// 手动加载微应用getMicroInfo() {const appIdentifying = this.$route.path.split('/')[1];let data = {};const href = window.location.host;for (let i = 0; i < document.subApps.length; i++) {const element = document.subApps[i];if (element.activeRule.includes(appIdentifying)) {if (typeof element.entry !== 'string') {data = {...element,entry: element.entry[href]? element.entry[href]: Object.values(element.entry)[0]};} else {data = { ...element };}data.props = {token: {userInfo: {userName: '小明',userId: '123',date: new Date().toLocaleString()}}};data.activeRule = [appIdentifying];break;}}return data;}}
};
</script>
1.2 index.html 引入配置
<!DOCTYPE html>
<html><head><meta charset="utf-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" /><metaname="viewport"content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/><link rel="icon" href="<%= BASE_URL %>favicon.ico" /><script src="<%= BASE_URL %>register-apps.js"></script><title><%= webpackConfig.name %></title></head><body><noscript><strong>We're sorry but <%= webpackConfig.name %> doesn't work properly withoutJavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><!-- built files will be auto injected --></body>
</html>
1.3 register-apps.js 配置
document.subApps = [{name: 'besFront',//entry: '//localhost:8086/bes-front/',// 本地调试entry: '/bes-front/',// 部署到服务器container: '#container-sub-app',activeRule: '/bes-front'}
];
1.4 路由配置
import Vue from 'vue';
import Router from 'vue-router';Vue.use(Router);import Layout from '@/layout';
import MicroApp from '@/components/microApp';export const constantRoutes = [{path: '/login',component: () => import('@/views/login/index'),hidden: true},{path: '/404',component: () => import('@/views/404'),hidden: true},{path: '/dashboard',component: Layout,redirect: '/dashboard/index',children: [{path: 'index',name: 'Dashboard',component: () => import('@/views/dashboard/index'),meta: { title: 'Dashboard', icon: 'dashboard' }}]},{path: '/',component: Layout,children: [{path: 'bes-front',component: MicroApp// 重点,用于加载子项目}]}// 404 page must be placed at the end !!!// { path: '*', redirect: '/404', hidden: true }
];const createRouter = () =>new Router({mode: 'history', // require service supportbase: '',scrollBehavior: () => ({ y: 0 }),routes: constantRoutes});const router = createRouter();// Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
export function resetRouter() {const newRouter = createRouter();router.matcher = newRouter.matcher; // reset router
}export default router;
2、子项目配置
2.1 main.js 配置
// 动态设置 publicPath
import './public-path';
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import store from './store';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';Vue.config.productionTip = false;
Vue.use(ElementUI);let instance = null;
function render(props = {}) {console.log('子应用 render props::', props, 'instance====', instance);// sessionStorage.setItem('userInfo', JSON.stringify(props.token.userInfo));const { container } = props;instance = new Vue({router,store,render: (h) => h(App)}).$mount(container ? container.querySelector('#app') : '#app');
}// 独立运行时
/* eslint-disable */
if (!window.__POWERED_BY_QIANKUN__) {render();
}export async function bootstrap() {console.log('子应用 bootstrap ===========================');
}let initialState = null;
export async function mount(props) {console.log('子应用 mount props ===============', props);sessionStorage.setItem('userInfo', JSON.stringify(props.token.userInfo));props.onGlobalStateChange((state, prev) => {// state: 变更后的状态; prev 变更前的状态console.log('子应用获取共享数据 state::', state, 'prev::', prev);// 接收主应用中的共享数据 并将其设置为全局变量Vue.prototype.$initialState = state;});props.setGlobalState({initialState:'子应用中修改主应用中的全局变量,实现住应用子应用间数据的双向双向通信'});render(props);
}
export async function unmount() {console.log('子应用 unmount==========');instance.$destroy();instance.$el.innerHTML = '';instance = null;
}
2.2 public-path 文件
if (window.__POWERED_BY_QIANKUN__) {/* eslint-disable @typescript-eslint/camelcase */__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
2.3 vue.config.js 配置
'use strict';
const path = require('path');
const defaultSettings = require('./src/settings.js');
const proxyTable = require('./proxyTable');function resolve(dir) {return path.join(__dirname, dir);
}const name = defaultSettings.title || 'vue Admin Template'; // page title// If your port is set to 80,// use administrator privileges to execute the command line.
// For example, Mac: sudo npm run
// You can change the port by the following methods:
// port = 9528 npm run dev OR npm run dev --port = 9528
const port = process.env.port || process.env.npm_config_port || 9528; // dev port// All configuration item explanations can be find in https://cli.vuejs.org/config/
module.exports = {/*** You will need to set publicPath if you plan to deploy your site under a sub path,* for example GitHub Pages. If you plan to deploy your site to https://foo.github.io/bar/,* then publicPath should be set to "/bar/".* In most cases please use '/' !!!* Detail: https://cli.vuejs.org/config/#publicpath*/publicPath: '/bes-front/',outputDir: 'dist',assetsDir: 'static',lintOnSave: process.env.NODE_ENV === 'development',productionSourceMap: false,devServer: {headers:{"Access-Control-Allow-Origin": "*",},port: port,open: true,proxy: proxyTable,overlay: {warnings: false,errors: true}},configureWebpack: {// provide the app's title in webpack's name field, so that// it can be accessed in index.html to inject the correct title.name: name,output:{library: `besFront`,// 主应用libraryTarget: "umd",// 把微应用打包成 umd 库格式jsonpFunction: `webpackJsonp_besFront`,// webpack5 需要把 jsonpFunction 替换成 chunkLoadingGlobal},resolve: {alias: {'@': resolve('src')}}},chainWebpack(config) {// it can improve the speed of the first screen, it is recommended to turn on preloadconfig.plugin('preload').tap(() => [{rel: 'preload',// to ignore runtime.js// https://github.com/vuejs/vue-cli/blob/dev/packages/%40vue/cli-service/lib/config/app.js#L171fileBlacklist: [/\.map$/, /hot-update\.js$/, /runtime\..*\.js$/],include: 'initial'}]);// when there are many pages, it will cause too many meaningless requestsconfig.plugins.delete('prefetch');// set svg-sprite-loaderconfig.module.rule('svg').exclude.add(resolve('src/icons')).end();config.module.rule('icons').test(/\.svg$/).include.add(resolve('src/icons')).end().use('svg-sprite-loader').loader('svg-sprite-loader').options({symbolId: 'icon-[name]'}).end();config.when(process.env.NODE_ENV !== 'development', (config) => {config.plugin('ScriptExtHtmlWebpackPlugin').after('html').use('script-ext-html-webpack-plugin', [{// `runtime` must same as runtimeChunk name. default is `runtime`inline: /runtime\..*\.js$/}]).end();config.optimization.splitChunks({chunks: 'all',cacheGroups: {libs: {name: 'chunk-libs',test: /[\\/]node_modules[\\/]/,priority: 10,chunks: 'initial' // only package third parties that are initially dependent},elementUI: {name: 'chunk-elementUI', // split elementUI into a single packagepriority: 20, // the weight needs to be larger than libs and app or it will be packaged into libs or apptest: /[\\/]node_modules[\\/]_?element-ui(.*)/ // in order to adapt to cnpm},commons: {name: 'chunk-commons',test: resolve('src/components'), // can customize your rulesminChunks: 3, // minimum common numberpriority: 5,reuseExistingChunk: true}}});// https:// webpack.js.org/configuration/optimization/#optimizationruntimechunkconfig.optimization.runtimeChunk('single');});}
};
3、主项目和子项目部署在同一台服务器上,其中主应用路由使用history模式,子应用路由使用hash模式,nginx 的配置如下:
locatoin / {root /opt/parent;index index.html index.htm;try_files $uri $uri/ /index.html;
}location /bes-front/ {root /opt/parent;index index.html index.htm;try_files $uri $uri. /index.html;
}
注:使用的下面这种部署方式,主应用代码放在parent目录下,子应用代码放在parent目录下面,其他,子应用的目录应该代码中的名称保持一致,在本示例当中,子应用使用的是bes-front
官网参考链接: 入门教程 - qiankun
4、总结
在部署主应用和子应用时,遇到主应用转发到子应用时,静态资源无法访问的问题,重点是查看子应用publicPath这个配置,这个是静态文件的访问前缀,如果部署到服务器上,子应用的静态资源无法访问到,可以看一下服务器访问的静态资源的前缀是否和代码中配置的一致。