微前端之qiankun的使用
在之前的文章中谈到了single-spa的使用,为了多了解微前端的各类架构,在新的项目中使用了qiankun,接下来就说一说qiankun的使用
安装
1、使用vue cli 快速创建基座(vue-portal)和子应用1(vue-supervise)、子应用2(vue-threen)
2、基座应用 npm install qiankun --save
准备工作做好后,我们开始分别对基座和子项目进行配置
基座应用(vue-portal)的配置
1、main.js 同级创建micro-app.js
import store from './store'
import store1 from './vuex'
const microApps = [
{
name: 'vthreen',
// entry: `http:XXXXXXX/vthreen/`, //测试环境地址
entry: '//localhost:8500/vthreen/', //本地运行地址
activeRule: '/vthreen',
container: '#yourContainer',
},
{
name: 'vsupervise',
// entry: `http:XXXXXXX/vsupervise/`, //测试环境地址
entry: 'http://localhost:8999/vsupervise/', //本地运行地址
activeRule: '/vsupervise',
container: '#yourContainer',
},
]
const apps = microApps.map(item => {
return {
...item,
container: '#yourContainer', // 子应用挂载的div
props: {
routerBase: item.activeRule, // 下发基础路由
getGlobalState: store.getGlobalState, // 下发getGlobalState方法(主应用的一些信息)
store1: store1.router || []
}
}
})
export default apps
2、main.js中使用
import Vue from 'vue'
import App from './App.vue'
import './assets/css/main.scss'
import {
registerMicroApps, start } from 'qiankun'
import ElementUI from 'element-ui'
import './router/engine'
import './utils/directive.js'
import * as components from './components'
import '@/assets/iconfont/iconfont.css' // fontclass使用
import '@/assets/iconfont/iconfont.js' //svg方式使用
import {
encrypt, decrypt } from '@/utils/encryp.js';
import store from './vuex'
import Vuex from 'vuex'
import router from './router'
Vue.config.productionTip = false
Vue.prototype.encrypt = encrypt
Vue.prototype.decrypt = decrypt
Vue.use(ElementUI)
Vue.use(Vuex)
import api from './api'
Vue.prototype.$api = api
Object.keys(components).forEach(key => {
const component = components[key]
if (['Col', 'Form', 'Input'].includes(key)) {
Vue.component(`I${
key}`, component)
} else {
Vue.component(key, component)
}
if (['Modal', 'Message', 'Notice'].includes(key)) {
Vue.prototype[`$${
key}`] = component
}
})
const instance = new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
import microApps from './micro-app'
// 定义loader方法,loading改变时,将变量赋值给App.vue的data中的isLoading
function loader(loading) {
if (instance && instance.$children) {
// instance.$children[0] 是App.vue,此时直接改动App.vue的isLoading
instance.$children[0].isLoading = loading
}
}
// 给子应用配置加上loader方法
const apps = microApps.map(item => {
return {
...item,
loader
}
})
registerMicroApps(apps, {
beforeLoad: app => {
},
beforeMount: [
app => {
}
],
afterMount: [
app => {
}
],
afterUnmount: [
app => {
}
]
})
start()
3、 页面挂载设置
在那个页面挂载就在该页面的mounted
<template>
<div id="app">
<el-container class="layout" v-if="!$route.meta.showAll">
<el-header>
<common-header :config="config"></common-header>
<ul class="header-nav">
<li class="menu-btn pointer" @click="goPortal">
<i class="el-icon-arrow-left"></i>返回首页
</li>
<li v-for="(sys, key) in sysList" class="pointer" :class="{'active': activeId == sys.appId}"
:key="key" @click="toggleSys(sys)">{
{
sys.appName}}</li>
</ul>
</el-header>
<router-view v-if="$route.meta.isSingle"></router-view>
<!-- yourContainer 挂载容器 -->
<div id="yourContainer" v-else></div>
</el-container>
<router-view v-else></router-view>
</div>
</template>
<script>
import {
mapGetters } from "vuex";
import {
CommonHeader } from "vue-common";
import {
start } from "qiankun";
import parser from 'vue-common/dist/router/parser'
import asyncRouter from '@/router/map/async.js'
export default {
name: 'App',
components: {
CommonHeader,
},
data() {
return {
modulesMenu: [],
sysList: [],
config: this.$config,
breadcrumbShow: false,
activeId: ""
};
},
computed: {
...mapGetters({
getSysList: 'getSysList',
}),
},
watch: {
getSysList(val) {
this.sysList = val
},
// 切换路由时监听导航渲染
'$route.path'(val) {
this.updateActiveRouter()
}
},
// 页面刷新时如果不是首页要加载侧边栏
mounted() {
if (!window.qiankunStarted) {
window.qiankunStarted = true;
start(); // 启动
}
},
methods: {
goPortal() {
this.$router.push({
name: 'home' })
}
},
created() {
// this.getConfigJson()
}
};
</script>
4、vue.config.js设置
module.exports = {
transpileDependencies: ['common'],
assetsDir: 'static',
lintOnSave: false, // 是否使用eslint
productionSourceMap: false, // 关闭生产源映射加速生产构建
devServer: {
port: 9999, // 在.env中VUE_APP_PORT=7788,与父应用的配置一致
headers: {
'Access-Control-Allow-Origin': '*' // 主应用获取子应用时跨域响应头
},
proxy: {
'/api': {
target: 'http://192.162.130.141:9001',
// target: 'http://172.160.1.4:9001',
changeOrigin: true, //开启代理
pathRewrite: {
'^/api': ''
}
},
},
https: false, // 不支持https协议
open: true, // 配置自动启动浏览器
},
chainWebpack: config => {
const oneOfsMap = config.module.rule('scss').oneOfs.store
oneOfsMap.forEach(item => {
item
.use('sass-resources-loader')
.loader('sass-resources-loader')
.options({
// 全局变量文件路径,只有一个时可将数组省去
resources: ['./src/assets/css/main.scss']
})
.end()
})
config.plugin('html')
.tap((args) => {
args[0].title = 'qiankun-example'
return args
})
}
}
5、路由设置
基座应用也是history模式,但是base不需要设置
子应用的配置
1、vue.config.js同级创建一个.env文件,端口号要与基座micro-app.js中的配置一致
2、main.js同级创建public-path.js
(function () {
if (window.__POWERED_BY_QIANKUN__) {
if (process.env.NODE_ENV === 'development') {
// eslint-disable-next-line
__webpack_public_path__ = `//localhost:${
process.env.VUE_APP_PORT}${
process.env.BASE_URL}`
return
}
// eslint-disable-next-line
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
// __webpack_public_path__ = `${process.env.BASE_URL}/`
}
})()
3、main.js配置
import './public-path'
let instance = null
function render(props = {
}) {
const {
container,
routerBase
} = props
// const router = new VueRouter({
// base: window.__POWERED_BY_QIANKUN__ ? routerBase : process.env.BASE_URL,
// mode: 'history',
// routes
// })
//路由配置
instance = new Vue({
router,
store,
render: (h) => h(App)
}).$mount(container ? container.querySelector('#vcatalog') : '#vcatalog')
}
if (!window.__POWERED_BY_QIANKUN__) {
// 这里是子应用独立运行的环境,实现子应用的登录逻辑
// 独立运行时,也注册一个名为global的store module
// commonStore.globalRegister(store)
// 模拟登录后,存储用户信息到global module
render()
}
export async function bootstrap() {
}
export async function mount(props) {
render(props)
}
export async function unmount() {
instance.$destroy()
instance.$el.innerHTML = ''
instance = null
}
4、路由配置放在router index.js中了
所有子项目必须是history模式,base设置与基座micro-app.js中的配置一致