主项目以及子项目都是VUE项目,两个项目路由:都为history
主应用引入qiankun 插件
安装qiankun
$ yarn add qiankun # 或者 npm i qiankun -S
在主应用当中注册微应用
//接入微服务
import { registerMicroApps, start } from 'qiankun';
new Vue({
router,
store,
i18n,
created: bootstrap,
render: h => h(App)
}).$mount('#app')
registerMicroApps([
{
name: 'qiankun-children',
entry: '//localhost:8081',
container: '#yourContainer',
activeRule: '/dome1',
sandbox: {
strictStyleIsolation: true
},
},
]);
if (!window.qiankunStarted) {
window.qiankunStarted = true;
start();
}
vue.config.js
devServer: {
port: 8008,
headers: { //配置需要添加跨域访问配置
'Access-Control-Allow-Origin': '*',
},
overlay: {
warning: false,
errors: false
}
},
子应用注册
代码如下:
let instance = null;
function render(props = {}) {
console.log(props)
const { container } = props;
instance = new Vue({
router,
store,
i18n,
render: (h) => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
}
if(__webpack_public_path__){
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
if(window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__){
console.log(window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__,window,"--") ;
}
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);
}
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
}
在启动以及打包、编辑可能遇到的问题
如下:
启动之后刷新页面子应用会消失
这是因为主应用和子应用两个id不能同时加载,需要异步处理子应用加载id的时间
if (!window.qiankunStarted) {
window.qiankunStarted = true;
setTimeout(()=>{
start();
},2000)
}
接入路由之后点击路由切换会出现问题
解决办法如下:
在子应用里面添加public-path.js
代码如下:
if (window.__POWERED_BY_QIANKUN__) {
// eslint-disable-next-line no-undef
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
在子路由配置router添加前缀
export default new Router({
mode: 'history',
base: window.__POWERED_BY_QIANKUN__ ? '/dome1' : '/',
routes: asyncRouterMap
})
在打包的时候会出现子应用$router找不到的问题。
qiankun 中的代码使用 Proxy 去代理父页面的 window,来实现的沙箱,在微应用中访问 window.Vue 时,会先在自己的 window 里查找有没有 Vue 属性,如果没有就去父应用里查找。
在 VueRouter 的代码里有这样三行代码,会在模块加载的时候就访问 window.Vue 这个变量,微应用中报这个错,一般是由于父应用中的 Vue 挂载到了父应用的 window 对象上了。
解决办法如下:
标题在主应用main.js添加如下代码**
window.Vue2=window.Vue
delete window.Vue
打包之后样式隔离问题,如何确保主应用跟微应用之间的样式隔离
通过配置 { sandbox : { experimentalStyleIsolation: true } }
的方式开启运行时的 scoped css 功能,从而解决应用间的样式隔离问题。
解决办法如下:
registerMicroApps([
{
name: 'qiankun-children',
entry: '//localhost:8081',
container: '#yourContainer',
activeRule: '/dome1',
sandbox: {
strictStyleIsolation: true //主应用添加隔离属性
},
},
]);
整体主应用代码
// with polyfills
import 'core-js/stable'
import 'regenerator-runtime/runtime'
import VueCodeMirror from 'vue-codemirror'
import 'codemirror/lib/codemirror.css'
import Vue from 'vue'
import App from './App.vue'
import {router} from './router'
import store from './store/'
import i18n from './locales'
import { VueAxios } from './utils/request'
import ProLayout, { PageHeaderWrapper } from '@ant-design-vue/pro-layout'
import themePluginConfig from '../config/themePluginConfig'
import { mixins } from '@/utils/mixins'
//接入微服务
import { registerMicroApps, start } from 'qiankun';
// 以下为bpmn工作流绘图工具的样式
import 'bpmn-js/dist/assets/diagram-js.css' // 左边工具栏以及编辑节点的样式
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css'
import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css'
import 'bpmn-js-properties-panel/dist/assets/bpmn-js-properties-panel.css' // 右边工具栏样式
// 图数据预览
import {vuePlugin} from "@/highlight";
import "highlight.js/styles/atom-one-dark-reasonable.css";
Vue.use(vuePlugin);
Vue.mixin(mixins)
import MagicContextMenu from './components/common/magic-contextmenu'
import Modal from './components/common/modal'
Vue.config.productionTip = false
Vue.use(MagicContextMenu)
Vue.use(Modal)
// mock
// WARNING: `mockjs` NOT SUPPORT `IE` PLEASE DO NOT USE IN `production` ENV.
// import './mock'
import bootstrap from './core/bootstrap'
import './core/lazy_use'
import './permission' // permission control
import './utils/filter' // global filter
import './global.less'
import './globalTheme.less'
//JSON展示插件
import JsonViewer from 'vue-json-viewer'
import 'vue-json-viewer/style.css'
Vue.use(JsonViewer)
//高亮显示代码编辑插件
import { codemirror } from 'vue-codemirror'
import 'codemirror/lib/codemirror.css'
Vue.use(codemirror)
//XML转换JSON插件
import x2js from 'x2js'
Vue.prototype.$x2js = new x2js()
//复制到粘贴板插件 VueClipboard该变量代码里搜不到 使用中 误删!
import VueClipboard from 'vue-clipboard2'
VueClipboard.config.autoSetContainer = true
Vue.use(VueClipboard)
//颜色选择器
import vcolorpicker from 'vcolorpicker'
//表格插件
import 'xe-utils'
import VXETable from 'vxe-table'
import 'vxe-table/lib/index.css'
import VXETablePluginAntd from 'vxe-table-plugin-antd'
import 'vxe-table-plugin-antd/dist/style.css'
Vue.use(VXETable)
VXETable.use(VXETablePluginAntd) //插件兼容ant-design-vue 配置
Vue.config.productionTip = false
// mount axios to `Vue.$http` and `this.$http`
Vue.use(VueCodeMirror)
Vue.use(vcolorpicker)
Vue.use(VueAxios)
Vue.component('pro-layout', ProLayout)
Vue.component('page-header-wrapper', PageHeaderWrapper)
window.umi_plugin_ant_themeVar = themePluginConfig.theme
window.Vue2=window.Vue
delete window.Vue
new Vue({
router,
store,
i18n,
created: bootstrap,
render: h => h(App)
}).$mount('#app')
registerMicroApps([
{
name: 'qiankun-children',
entry: '//localhost:8081',
container: '#yourContainer',
activeRule: '/dome1',
sandbox: {
strictStyleIsolation: true
},
},
]);
if (!window.qiankunStarted) {
window.qiankunStarted = true;
setTimeout(()=>{
start();
},2000)
}
子应用代码
// with polyfills
import './public-path'
import 'core-js/stable'
import 'regenerator-runtime/runtime'
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store/'
import i18n from './locales'
import { VueAxios } from './utils/request'
import ProLayout, { PageHeaderWrapper } from '@ant-design-vue/pro-layout'
import themePluginConfig from '../config/themePluginConfig'
// mock
// WARNING: `mockjs` NOT SUPPORT `IE` PLEASE DO NOT USE IN `production` ENV.
// import './mock'
// import bootstrap from './core/bootstrap'
import './core/lazy_use' // use lazy load components
import './permission' // permission control
import './utils/filter' // global filter
import './global.less' // global style
Vue.config.productionTip = false
import 'xe-utils'
import VXETable from 'vxe-table'
import 'vxe-table/lib/style.css'
Vue.use(VXETable)
// mount axios to `Vue.$http` and `this.$http`
Vue.use(VueAxios)
// use pro-layout components
Vue.component('pro-layout', ProLayout)
Vue.component('page-container', PageHeaderWrapper)
Vue.component('page-header-wrapper', PageHeaderWrapper)
window.umi_plugin_ant_themeVar = themePluginConfig.theme
// new Vue({
// router,
// store,
// i18n,
// // init localstorage, vuex, Logo message
// created: bootstrap,
// render: h => h(App)
// }).$mount('#app')
let instance = null;
function render(props = {}) {
console.log(props)
const { container } = props;
instance = new Vue({
router,
store,
i18n,
render: (h) => h(App),
}).$mount(container ? container.querySelector('#app') : '#app');
}
if(__webpack_public_path__){
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
if(window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__){
console.log(window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__,window,"--") ;
}
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);
}
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
}