一、主应用
1.提供一个容器
<div id="sub"></div>
2.注册子服务
import { initGlobalState, registerMicroApps, setDefaultMountApp, start } from 'qiankun'
registerMicroApps(finalApps.value, {
beforeMount: (app) => {
const appItem = finalApps.value.find((v) => {
return v.name === app.props.name
})
app.props = appItem.props
nProgress.start()
},
afterMount: () => {
nProgress.done()
}
})
start({
prefetch: 'all'
})
if (apps.length > 0) {
setDefaultMountApp(apps[0].activeRule)
}
finalApps.value:
[
{
activeRule: "/hbHome"
alwaysShow: false
container: "#sub"
entry: "/microBtHome/"
id: 97
name: "HomePage",
props: {...}
},
{
activeRule: "/hbUser"
alwaysShow: false
container: "#sub"
entry: "/microUser/"
id: 98
name: "UserManagement",
props: {...}
}
]
registerMicroApps(apps, lifeCycles?)
apps:
1.name 必选,微应用的名称,微应用之间必须确保唯一
2.entry 必选,微应用的入口。
- 配置为字符串时,表示微应用的访问地址,例如 `https://qiankun.umijs.org/guide/`。
- 配置为对象时,`html` 的值是微应用的 html 内容字符串,而不是微应用的访问地址。微应用的 `publicPath` 将会被设置为 `/`
3.container 必选,微应用的容器节点的选择器或者 Element 实例
- 如`container: '#root'` 或 `container: document.querySelector('#root')`。
4.activeRule
- 支持直接配置字符串或字符串数组,如 `activeRule: '/app1'` 或 `activeRule: ['/app1', '/app2']`,当配置为字符串时会直接跟 url 中的路径部分做前缀匹配,匹配成功表明当前应用会被激活。
- 支持配置一个 active function 函数或一组 active function。函数会传入当前 location 作为参数,函数返回 true 时表明当前微应用会被激活。如 `location => location.pathname.startsWith('/app1')`。
5.loader(function) 可选,loading 状态发生变化时会调用的方法。
6.props(`object`) 可选,主应用需要传递给微应用的数据
LifeCycles:
- beforeLoad - `Lifecycle | Array<Lifecycle>` - 可选
- beforeMount - `Lifecycle | Array<Lifecycle>` - 可选
- afterMount - `Lifecycle | Array<Lifecycle>` - 可选
- beforeUnmount - `Lifecycle | Array<Lifecycle>` - 可选
- afterUnmount - `Lifecycle | Array<Lifecycle>` - 可选
二、微应用
入口文件 main.js 修改,为了避免根 id #app 与其他的 DOM 冲突,需要限制查找范围。
1. main.js中导出相应的生命周期钩子
import { createApp } from 'vue'
import App from './App.vue'
import routes from './router'
import store from './store'
import { createRouter, createWebHistory } from 'vue-router
import generateRoutes from './utils/generateRouter'
let app = null
let router
if (window.__POWERED_BY_QIANKUN__) {
// 动态设置 webpack publicPath,防止资源加载出错
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__
} else {
router = createRouter({
history: createWebHistory('/'),
routes
})
app = createApp(App)
installElementPlus(app)
installSvg(app)
app
.use(store)
.use(router)
.mount('#sub-app')
}
//解析从主模块传递的数据
function render (props) {
let finalRoutes
if (process.env.NODE_ENV === 'development') {
finalRoutes = routes
} else {
finalRoutes = generateRoutes(props.resources, routes)
}
const find = finalRoutes.filter(item => !item.hidden)
if (finalRoutes.length > 0) {
finalRoutes = [
{
path: '/',
hidden: true,
redirect: find[0].path
},
...finalRoutes
]
}
store.commit('router/SET_ROUTES', finalRoutes)
router = createRouter({
history: createWebHistory(props.baseUrl),
routes: finalRoutes
})
app = createApp(App)
app
.use(store)
.use(router)
.mount('#sub-app')
}
export const bootstrap = async () => {
console.log('微服务已挂载')
}
export const mount = async props => {
store.commit('router/SET_APP_NAME', props.appName)
store.commit('router/SET_BASE_URL', props.baseUrl)
store.dispatch('user/setUserInfo', props.userInfo)
store.commit('user/SET_ALL_RESOURCES', props.allResources)
store.commit('user/SET_GALOBAL_STATE', props)
store.commit('api/SET_BASE_API', 'props.baseApi')
props.onGlobalStateChange(state => {
store.dispatch('user/setUserInfo', state)
store.commit('router/SET_HISTORYLIST', state.historyList)
store.commit('collapse/SET_COLLAPSE', state.isCollapse)
})
render(props)
}
export const unmount = async () => {
app.unmount()
console.log('微服务已卸载')
// app.unmount('#sub-app')
app = null
}
generateRouter.js文件
function hasPermission(names, route) {
if (route.hidden) return true
if (route.alwaysShow) return true
if (route.name) return names.includes(route.name)
}
function generateRoutes(names, routes) {
const arr = []
routes.forEach(item => {
const map = { ...item }
if (hasPermission(names, map)) {
if (map.children) {
map.children = generateRoutes(names, map.children)
}
arr.push(map)
}
})
return arr
}
export default generateRoutes
finalRoutes:
[
{
"path": "/baseConfig",
"name": "crmMarketing.baseConfig",
"meta": {
"name": "基础数据"
}
},
{
"path": "/salesVolume",
"name": "crmMarketing.salesVolume",
"meta": {
"name": "年度目标额"
}
},
{
"path": "/attendance",
"name": "crmMarketing.attendance",
"meta": {
"name": "考勤管理"
}
},
{
"path": "/currentOnline",
"name": "crmMarketing.currentOnline",
"meta": {
"name": "在线统计"
}
},
{
"path": "/trip",
"name": "crmMarketing.trip",
"meta": {
"name": "行程管理"
}
},
{
"path": "/marketShare",
"name": "crmMarketing.marketShare",
"meta": {
"name": "市场占有率"
}
},
{
"path": "/bannerConfig",
"name": "crmMarketing.bannerConfig",
"meta": {
"name": "移动端导航"
}
},
{
"path": "/feedback",
"name": "crmMarketing.feedback",
"meta": {
"name": "信息反馈"
}
},
{
"path": "/noticeConfig",
"name": "crmMarketing.noticeConfig",
"meta": {
"name": "云盘通知"
}
},
{
"path": "/:pathMatch(.*)",
"name": "404",
"meta": {
"name": "404"
},
"hidden": true
}
]
2. 配置微应用的打包工具
webpack看官网: qiankun.umijs.org/zh/guide/ge…
vue.config.js中配置
const packageName = require('./package.json').name;
module.exports = {
configureWebpack: {
output: {
library: `${packageName}-[name]`,
libraryTarget: 'umd'
}
}
}
三、主应用跳转不同子应用
1.主应用通过history.pushState方式
// url 为当前子应用的activeRule
function usePush(url) {
history.pushState('', '', url)
}
2.子应用通过配置history来匹配主应用
//props.baseUrl 主应用传递过来的当前子应用的activeRule
let router
router = createRouter({
history: createWebHistory(props.baseUrl),
routes: finalRoutes
})
浏览器的 url 发生变化会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子。