持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
前言
摸鱼实在是摸得慌了,动手记录最近公司落地的微前端框架所遇到的诸多问题。
本文主要记录公司实现父子应用切换tabs时缓存页面的解决方案
如有什么不对的,欢迎指出~
微前端是什么?微前端有什么用...
这里就不介绍了,还请各位自行百度吧!
项目框架
vue + qiankun2.0 + vuex + vue-router + elementUi
主应用和子应用都是使用了vue,路由统一使用了Hash模式
原理
监听页面路由,判断是否是子应用路由(子应用路由有特定前缀micro)并且是否没有加载过子应用,是则手动加载子应用。 移除tab时,判断是否是子应用菜单并且在tabList中是否还有子应用的菜单,如果没有子应用菜单, 则执行unmount方法卸载子应用,如果有子应用菜单,则执行子应用提供的update方法清除该子应用的组件缓存
实现
主应用
通过监听主应用路由的beforeEach方法来动态加载(loadMicroApp)微应用页面,使用vuex管理已加载的tab标签和微应用数据
router.js
...
const router = createRouter({
history: createWebHashHistory(process.env.BASE_URL),//hash模式
routes
})
// 全局前置守卫
router.beforeEach((to, _form, next) => {
if (to.fullPath === _form.fullPath) {
return
}
store.dispatch('openTab', to) //调用tab打开新标签方法
next()
})
store/tabs.js
import router from '@/router/index.js'
import { loadMicroApp, initGlobalState } from 'qiankun'
// state
const state = {
microAppMap: null,
tabsList: [],
currentTab: '-1',
activeMenuPath: '',
welcomeTab: {
url: '/welcome',
permId: '-1',
permCode: 'welcome',
permName: '欢迎页'
}
}
// actions
const actions = {
openTab({ rootState, dispatch }, to) {
const currentPath = to.meta.activePath || to.path
// 获取菜单列表数据
const allFunctionList = rootState.user.user.funPerms || []
// 获取url对应的菜单item数据
const currentTabItem = allFunctionList.find(i => i.url === currentPath)
if (currentPath !== state.welcomeTab.url) {
dispatch('addTab', currentTabItem)
} else {
dispatch('addTab', state.welcomeTab)
}
if (currentPath.includes('/micro')) {
dispatch('loadMicroApp')
}
},
// 添加tab
addTab({ commit }, tabItem) {
//是否有打开标签
if (!state.tabsList.some(t => t.permId === tabItem.permId)) {
commit('ADD_TAB', tabItem)
}
commit('SET_CURRENT_TAB', tabItem)
},
// 点击tab
selectTab({ commit }, tabIndex) {
const currentTab = state.tabsList[tabIndex]
commit('SET_CURRENT_TAB', currentTab)
router.replace({ path: currentTab.url })
},
// 移除tab
removeTab({ commit, dispatch }, tabVal) {
const index = state.tabsList.findIndex(item => item.permId === Number(tabVal))
const currentPath = state.tabsList[index].url //当前tab标签的地址
const previousPath = state.tabsList[index - 1].url //上一个tab标签的地址
commit('SET_CURRENT_TAB', state.tabsList[index - 1])
commit('REMOVE_TAB', index)
router.replace({ path: previousPath })
if (currentPath.includes('/micro')) {
// 如果是子应用地址,触发卸载子应用action
dispatch('unloadMicroApp', currentPath)
}
},
// 加载微应用
async loadMicroApp({ state, rootState, getters, dispatch }) {
// 如果还未加载过,则手动加载微应用
if (state.microAppMap === null) {
state.microAppMap = loadMicroApp({
name: 'micro-admin',
entry: 'http://localhost:3333',//子应用地址
container: '#appContainer'
})
}
},
// 卸载微应用
unloadMicroApp(_, path) {
// 如果当前tablist没有微应用菜单,则卸载微应用
if (!state.tabsList.some(item => item.url.includes('/micro'))) {
try {
state.microAppMap.unmount()
state.microAppMap = null
} catch (error) {
console.log(error)
}
} else {
// 否则执行update方法,子应用去除该模块keep-alive缓存
state.microAppMap.update({
routerEvent: {
path: path,
type: 'close'
}
})
}
}
}
// mutations
const mutations = {
['ADD_TAB'](state, data) {
state.tabsList.push(data)
},
['REMOVE_TAB'](state, index) {
state.tabsList.splice(index, 1)
},
['SET_CURRENT_TAB'](state, tabItem) {
state.currentTab = tabItem.permId.toString()
state.activeMenuPath = tabItem.url
}
}
export default {
state,
actions,
mutations
}
子应用
组件开启keep-alive,并且在qiankun提供的生命周期钩子update里调用子应用删除缓存的方法 需要传入一个routeEvent对象,用于清除子应用组件keep-alive缓存
main.js
qiankun渲染方法
function render(props = {}) {
const { container } = props
// const history = createWebHashHistory(
// window.__POWERED_BY_QIANKUN__ ? `/micro-account` : '/'
// )
router = createRouter({
history: createWebHashHistory(process.env.BASE_URL),
routes
})
router.beforeEach((to, form, next) => {
// 如果有name则是子应用路由,没有则是父应用路由
if (to.name) {
store.commit('PUSH_KEEPALIVE_LIST', to.name)
}
next()
})
// update方法去除子节点菜单的keep-alive状态
export async function update(props) {
let { routerEvent } = props
if (routerEvent) {
switch (routerEvent.type) {
case 'close':
{
store.commit(
'CLOSE_KEEPALIVE_LIST',
routes.find(item => item.path === routerEvent.path)
)
}
break
}
}
}
store/tabs.js
const state = {
keepAliveList: []
}
const mutations = {
['PUSH_KEEPALIVE_LIST'](state, val) {
if (!state.keepAliveList.find(item => item === val)) {
state.keepAliveList.push(val)
}
},
['CLOSE_KEEPALIVE_LIST'](state, val) {
state.keepAliveList = state.keepAliveList.filter(item => item !== val.name)
}
}
export default {
state,
mutations
}
最后
项目已经上线了。 如果你也需要实现微应用多tab标签页路由切换,可以尝试一下,如有问题可以找我。
接下来还写一下其他问题的解决方案~
请期待