写在前面:微服务风生水起,微前端也随之而来。最近开发一大型erp系统,决定使用微前端框架,看过几个大厂的各种微前端框架,最后决定使用qiankun。主要是相比于其他框架,qiankun 的生态更丰富一些,部分其他大厂的文档写的实在是一眼难尽。也可能是小弟学艺不精,不能感受到文档的玄妙之处
以下文档以vue框架作为基础框架
微前端qiankun配置
配置内容我将从以下几个点进行分开梳理
- 主应用基座配置
- 子应用配置
- 主子应用通信问题处理
- 关于nginx配置发布 内容较多,且容易遗漏
主应用基座配置
主要内容:
- 初始化应用,安装qiankun依赖包
- 注册子应用信息
- 修改路由模式(history/hash)
- 我这里使用ant-design-pro作为基础框架
git clone --depth=1 https://github.com/vueComponent/ant-design-vue-pro.git main
- 安装qiankun依赖包
yarn add qiankun
- 注册子应用(qiankun提供两种api) 方式一:registerMicroApps
//main.js
import { registerMicroApps } from 'qiankun'
registerMicroApps([
{
name: 'childone',
entry:'http://10.100.72.158:8080/qks/',
container: '#childone',
activeRule: '/mainqks/childone'
},
......
])
方式二:loadMicroApp
if (!window.qiankunStarted) {
window.qiankunStarted = true
this.microApp = loadMicroApp(
{
name: 'setpro',
entry: 'http://your ip/setpro/page1',
container: '#setpro'
},
{
fetch(url, ...args) {
return window.fetch(url, ...args)
}
}
)
}
- 新增一个容器页面,用于挂载子应用,start指启动挂载子应用的api
<!-- 子容器挂载区域 -->
<div class="child-area">
<div id="res"></div>
<div id="ordsys"></div>
<div id="fin"></div>
<div id="crm"></div>
<div id="playmini"></div>
</div>
...
...
...
import { start } from 'qiankun'
...
start()
- 在路由中添加此页面的路由
import Qks from '../views/qiankuns/index.vue'
{
path: '/mainqks',
name: 'mainqks',
component: Qks
},
- 修改原有ant的路由模式,由后端维护获取改为从后端拿权限,与前端维护的路由进行匹配后store/index.js
ant-design-pro 提供两种权限方式,详见其文档
- 修改package.json中的name属性,保证项目的名称唯一
"name": "your pro name",
- ant-design-vue-pro中使用mock,如果要进行发布预览查看效果,需要修改.env的VUE_APP_PREVIEW,由false改为true
VUE_APP_PREVIEW=true
子应用基础配置
主要内容:
- 克隆基础项目(这里指ant-design-pro)
- 导出相应生命周期钩子
- 配置public-path.js
- 配置微应用打包(主要涉及vue.config.js)
- 配置devServer
- 配置mock预览
- 克隆基础项目
git clone --depth=1 https://github.com/vueComponent/ant-design-vue-pro.git child-one
- 导出相应生命周期钩子(这里的actions是我封装的通信工具,后面会提到)
//main.js
export async function bootstrap () {
console.log('[vue] vue app bootstraped')
}
export async function mount (props) {
console.log('[vue] props from main framework', props)
// 注入actions实例
actions.setActions(props)
render(props)
}
export async function unmount () {
instance.$destroy()
instance.$el.innerHTML = ''
instance = null
router = null
}
- src下新增public-path.js文件,需要在main.js中引入
/* eslint-disable */
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
}
main.js中引入
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
- 配置微应用作为子包供基座接入
//vue.config.js
const packageName = require('./package.json').name;
module.exports = {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd',
jsonpFunction: `webpackJsonp_${packageName}`,
},
};
- devServer配置header
devServer:{
// qiankun相关配置
headers: {
'Access-Control-Allow-Origin': '*'
}
}
- 特殊的变量名称修改 bootstrap与qiankun的钩子函数名称重名,这边对它进行重新命名
router有一个销毁重新赋值的过程,对它进行重新命名
- 完整的main.js代码
// with polyfills
import './public-path'
import 'core-js/stable'
import 'regenerator-runtime/runtime'
import Vue from 'vue'
import App from './App.vue'
import childRouter 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 antBootstrap 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
import actions from './actions/index'
Vue.config.productionTip = false
// 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
let router = null
let instance = null
function render (props = {}) {
const { container } = props
router = childRouter
instance = new Vue({
router,
store,
i18n,
created: antBootstrap,
render: (h) => h(App)
}).$mount(container ? container.querySelector('#app') : '#app')
}
// 独立运行时
if (!window.__POWERED_BY_QIANKUN__) {
render()
}
export async function bootstrap () {
console.log('[vue] vue app bootstraped')
}
export async function mount (props) {
console.log('[vue] props from main framework', props)
// 注入actions实例
actions.setActions(props)
render(props)
}
export async function unmount () {
instance.$destroy()
instance.$el.innerHTML = ''
instance = null
router = null
}
补充vue3.x相关配置
- vue.config.js中修改打包输出路径和publicPath,这个要与路由的base保持一致 如基座中子应用配置history路由的标识符为childone,则
9. 修改原有ant的路由模式,由后端维护获取改为从后端拿权限,与主应用中保持一致
10. 修改.env文件VUE_APP_PREVIEW,与主应用保持一致
11. 修改package.json 理由同主应用中配置
可能遇到的路由问题
- 路由跳转失败、404、反复横跳等,路由相关错误 解决方式:
举例:
- 主子应用都为history模式
- 主应用中注册方式如下:
{
name: 'childsix',
entry: 'http://your ip/nginxtest/',
container: '#childsix',
activeRule: '/nginxtest/'
},
当浏览器中路径变为 <http://your ip/nginxtest/> 时,即触发子应用挂载,这里的/nginx/需要与子应用中设置的router的base相同,即如下:
export default new Router({
base: '/nginxtest/',
mode: 'history',
routes: constantRouterMap
})
当然子应用中的publicPath 也要随之修改 这里的nginxtest标识符需要在主应用中设置通配符,如下,否则,主应用将会无法识别路由而跳转404,这是很容易忘记的一点
{
path: '/nginxtest/*',
component:TestPage
},