背景说明
-
基座
mainApp,子应用cust,contact全部都是基于codeWave搭建 -
所有子应用都开启无界的保活模式
具体场景
基座首次进入子应用
// 由于子应用都开启了保活模式所以需要采用如下方式处理跳转(具体看下图)
// 主应用路由实例进行跳转
this.$router.push(`${prefix}`/`${subAppcode}`?`${subAppcode}`=`${fullPath}`)
子应用内部跳转(子应用已经实例化)
//内部跳转直接路由方法跳转
this.$router.push(url)
注意:vueRouter3中导航重复路由path会报警告,我们需要特殊处理一下;特别是项目中不支持动态路由的情况。
跨应用跳转
cust往contact跳转(/m/cust/list到/m/contact/detail?id=1&title=张三)
contact应用是否已经实例化,- 如果已经实例化则再判断
contact应用的curentRoute元信息的path和要去的页面的path是否一样,如果一样的话需要特殊处理 contact应用没有实例化,直接使用通信的方式告诉主应使用路由跳转(类比基座首次进入子应用场景)
- 如果已经实例化则再判断
记录多页签(类似于浏览器的多页签)
基于通信告诉主应用,主应用维护一个tagList,定义好Dto,然后使用LRU算法移除最早未使用的页签等,可参考如下页签管理类
浏览器回退相关场景
- 开启
keep-alive后,回退后需要销毁from页对应的vue页面实例
//思路1、思路是缓存的必须得是每个.vue页面的default导出内容,通过调用vue的destroy方法销毁对应实例(需要对导出的源码操作)
// 思路2、如下
<template>
<!-- 通过 ref 获取 keep-alive 实例 -->
<keep-alive ref="keepAliveRef">
<!-- 路由视图,通过 $route.fullPath 确保路由切换时触发组件重建 -->
<router-view :key="$route.fullPath"></router-view>
</keep-alive>
</template>
<script>
export default {
methods: {
clearCacheByKey(key) {
// 获取 keep-alive 实例的缓存对象和键数组
const { cache, keys } = this.$refs.keepAliveRef;
if (cache[key]) {
// 销毁组件实例,清理缓存
cache[key].componentInstance.$destroy();
delete cache[key];
const index = keys.indexOf(key);
if (index > -1) keys.splice(index, 1);
}
}
}
}
</script>
- 回退后需要拿到当前激活的路由信息
//实现思路:
1、首先需要记录当前激活的应用唯一标识
2、每个应用首次进入时需要将自身的路由实例告诉基座
3、基座维护一个路由实例的map
4、基座通过子应用标识去获取子应用路由实例,并取到对应的路由元信息
- 回退后刷新
to页面的列表等操作(一般来说只要不keep-alive就都会刷新,主要解决keep-alive后)
//实现思路1:
1、列表页面约定一个标识`flag`标记标记此页面是否已经挂载过
2、基座维护一个是否需要刷新的标识`isRefrsh`
3、利用`activeted`钩子,在页面激活的时候触发约定的`refresh`方法
//实现思路2:
1、保证缓存的`<component>`的对应的`.vue`页面的默认导出,可用node.js的require语法导入页面
2、页面有唯一的标识通过`ref`调用页面的方法实现刷新
路由跳转实现思路(伪代码)
- 基座定义一个注册子应用路由的方法并挂载到基座的
window对象上通过props传递给子应用,用来判断子应用是否已经实例化 - 子应用注册监听事件
window.$wujie.bus.$on('routeChange',(urlObj)=>{})
- 基座定义
jump方法
jump(urlObj){
const findCurRoute = window.VueRouterInstance.currentRoute
const find = window.routeManager.subAppRouterInstances.get(urlObj.code)
if (find) {
// 要去页面对应的应用已经实例化
window.$wujie.bus.$emit('routeChange', urlObj)
} else {
// 要去页面对应应用没有实例化
if (findCurRoute && findCurRoute.path === urlObj.resourcesValue) {
// 出现此种情况是有问题的,一般不会出现
} else {
//直接调用
this.$router.push(urlObj.fullPath)
}
}
}
其他技巧
- 路由后置守卫触发wujie
eventbus事件 - 路由前置守卫做用户页面权限处理
多页前管理类实现(仅供参考)
// 多页签管理类
class SimpleTabManager {
constructor(options = {}) {
this.options = {
maxTabs: 10,
onTabChange: () => { },
...options
};
// 初始状态包含首页
this.state = {
tabs: [],
activeTabId: '',
tabHistory: []
};
}
// -------------------- 核心API --------------------
addTab(RouterTagDto) {
const onlyKey = RouterTagDto.onlyKey;
// 存在性检查
const existingTab = this.getTab(onlyKey);
if (existingTab) {
this.switchTab(onlyKey);
return existingTab;
}
// 容量控制
if (this.state.tabs.length >= this.options.maxTabs) {
this.removeOldestTab();
}
// 创建新页签
const newTab = this.createTabObject(RouterTagDto);
this.state.tabs = [...this.state.tabs, newTab];
this.switchTab(onlyKey);
return newTab;
}
removeTab(onlyKey, options = { force: false }) {
const closedTab = this.getTab(onlyKey);
if (!closedTab) return null;
// 保护最后一个页签
if (!options.force && this.state.tabs.length === 1) {
return null;
}
const wasActive = this.isTabActive(onlyKey);
// 执行删除
this.state.tabs = this.state.tabs.filter(t => t.onlyKey !== onlyKey);
this.purgeHistory(onlyKey);
let activatedTab = null;
if (wasActive) {
activatedTab = this.activateFallbackTab();
}
// 触发回调
this.options.onTabChange(
'remove',
closedTab,
this.state.tabs,
wasActive ? { newActiveTab: activatedTab } : null
);
return wasActive ? activatedTab : null;
}
updateTag(onlyKey, RouterTagDto) {
if (RouterTagDto && RouterTagDto.onlyKey) {
const tab = this.getTab(onlyKey)
const oldTab = { ...tab }
Object.assign(tab, RouterTagDto)
this.state.activeTabId = tab.onlyKey
this.triggerTabChange('update', RouterTagDto, oldTab)
} else {
console.warn('Cannot update the onlyKey of a tab.');
}
}
/**
* 删除除指定onlyKey外的所有页签
* @param {string} onlyKeyToKeep 需要保留的页签的唯一标识
*/
removeAllExcept(onlyKeyToKeep) {
// 检查要保留的页签是否存在
const tabToKeep = this.getTab(onlyKeyToKeep);
if (!tabToKeep) {
console.warn(`Tab with onlyKey ${onlyKeyToKeep} not found`);
return null;
}
// 筛选出需要删除的页签
const tabsToRemove = this.state.tabs.filter(tab => tab.onlyKey !== onlyKeyToKeep);
// 更新状态:只保留指定的页签
this.state.tabs = [tabToKeep];
// 清理历史记录:只保留指定页签的记录
this.state.tabHistory = this.state.tabHistory.filter(id => id === onlyKeyToKeep);
// 如果当前活动页签被删除,则激活保留的页签
if (!this.isTabActive(onlyKeyToKeep)) {
this.state.activeTabId = onlyKeyToKeep;
tabToKeep.lastActive = Date.now();
}
// 触发通知
this.options.onTabChange(
'removeAllExcept',
tabToKeep,
this.state.tabs,
{
removedTabs: tabsToRemove,
keptTab: tabToKeep
}
);
return tabToKeep;
}
removeByOnlyKey(onlyKey) {
return this.removeTab(onlyKey)
}
removeActiveTab() {
return this.removeTab(this.state.activeTabId)
}
switchTab(onlyKey) {
const oldTab = this.getActiveTab();
// 处理空值情况
if (!onlyKey || !this.hasTab(onlyKey)) {
this.state.activeTabId = null;
this.triggerTabChange('switch', null, oldTab);
return { to: null, from: oldTab };
}
const newTab = this.getTab(onlyKey);
// 更新激活状态
newTab.lastActive = Date.now();
this.state.activeTabId = onlyKey;
// 维护历史记录
this.updateHistory(onlyKey);
this.triggerTabChange('switch', newTab, oldTab);
return { to: newTab, from: oldTab };
}
// -------------------- 工具方法 --------------------
createTabObject(RouterTagDto) {
return {
code: RouterTagDto.code,
onlyKey: RouterTagDto.onlyKey,
title: RouterTagDto.title,
resourcesValue: RouterTagDto.resourcesValue,
fullPath: RouterTagDto.fullPath,
lastActive: Date.now(),
busAppCode: RouterTagDto.busAppCode,
activeMenuId: RouterTagDto.activeMenuId
};
}
setDeafautTab(RouterTagDto) {
const newTab = this.createTabObject(RouterTagDto);
this.state.tabs = [...this.state.tabs, newTab];
this.state.tabHistory = [RouterTagDto.onlyKey]
this.state.activeTabId = RouterTagDto.onlyKey
this.options.onTabChange('init', newTab, this.state.tabs, {
to: newTab,
from: null
});
}
getTab(onlyKey) {
return this.state.tabs.find(t => t.onlyKey === onlyKey);
}
hasTab(onlyKey) {
return this.state.tabs.some(t => t.onlyKey === onlyKey);
}
isTabActive(onlyKey) {
return this.state.activeTabId === onlyKey;
}
getActiveTab() {
return this.getTab(this.state.activeTabId);
}
// -------------------- 历史记录管理 --------------------
updateHistory(onlyKey) {
this.state.tabHistory = [
...this.state.tabHistory.filter(id => id !== onlyKey),
onlyKey
].slice(-this.options.maxTabs);
}
purgeHistory(onlyKey) {
this.state.tabHistory = this.state.tabHistory.filter(
id => id !== onlyKey
);
}
// -------------------- 辅助操作 --------------------
removeOldestTab() {
const oldest = [...this.state.tabs]
.sort((a, b) => a.lastActive - b.lastActive)[0];
if (oldest) this.removeTab(oldest.onlyKey);
}
activateFallbackTab() {
// 获取有效历史记录
const validHistory = this.state.tabHistory
.filter(id => this.hasTab(id));
// 尝试获取最近可用页签
const lastActiveId = validHistory.length > 0
? validHistory[validHistory.length - 1]
: this.state.tabs[0]?.onlyKey;
if (lastActiveId) {
return this.switchTab(lastActiveId).to;
}
return null;
}
triggerTabChange(action, newTab, oldTab) {
this.options.onTabChange(action, newTab, this.state.tabs, {
to: newTab,
from: oldTab
});
}
}
// 初始化实例
const tagManager = new SimpleTabManager({
maxTabs: 10,
onTabChange: (action, tab, tabList, changeObj) => {
console.log('页签状态变更:', action, tab?.onlyKey, tabList);
console.log('@@@@当前所有页签', _this.$global.frontendVariables.routerTagList)
}
});
window.tagManager = tagManager;