keep-alive 多级路由缓存问题 因为只支持二级路由缓存 三级路由无法支持 网上也有大佬的总结 就是三级也能缓存实际缓存的是二级 假如二级有两个三级子集 缓存就会出问题。所以说keep-alive只支持二级路由缓存。
1、因为左侧菜单栏强依赖路由数据
因为左侧菜单栏强依赖路由数据 于是 把路由元数据和菜单栏数据解耦 分成两个数据 左侧菜单栏数据(menuRoutes) 和route路由里实际使用的路由数据(routes)
state: {
routes: [], // 路由
menuRoutes: [], // 菜单路由
},
2、因为route路由当前是三层无法缓存
因为route路由当前是三层无法缓存 于是 把接口返回的三层路由 打平 打到二层去 并把三级路由的二级目录路由干掉。
// 遍历后台传来的路由字符串,转换为组件对象 =》菜单路由
function filterMenuRouter(asyncRouterMap) {
return asyncRouterMap.filter((route) => {
if (route.component) {
// Layout组件特殊处理
if (route.component === "Layout") {
route.component = Layout;
} else {
route.component = loadLazyView(route.component);
}
}
if (route.children != null && route.children && route.children.length) {
route.children = filterMenuRouter(route.children);
}
return true;
});
}
// 遍历后台传来的路由字符串,转换为组件对象=》实际路由
function filterAsyncRouter(asyncRouterMap) {
return asyncRouterMap.filter((route) => {
if (route.component) {
// Layout组件特殊处理
if (route.component === "Layout") {
route.component = Layout;
} else {
route.component = loadLazyView(route.component);
}
}
if (route.children != null && route.children && route.children.length) {
if (route.children) {
let temp = [];
for (const child in route.children) {
const temp_child = route.children[child];
if (temp_child.children) {
for (let i in temp_child.children) {
const t2 = temp_child.children[i];
const temp_child2 = { ...t2 };
temp_child2.path = temp_child.path + "/" + t2.path;
temp.push(temp_child2);
}
delete route.children[child];
}
}
route.children = route.children.concat(temp);
}
route.children = filterAsyncRouter(route.children);
}
return true;
});
}
3、路由数据与菜单栏数据解耦之后
路由数据与菜单栏数据解耦之后 出现一个新问题 因为面包屑强依赖于路由的to.matched 因为三级路由 可以直接动态依赖 matched来做面包屑 但是路由数据打平之后 三层路由会少一层面包屑 此处需要递归处理 从菜单路由数据里 取出 类似于to.matched数据 实现面包屑问题
// 获取第一层路由对象
findFirstRoute(route) {
let routes = this.$store.state.permission.menuRoutes;
for (let i in routes) {
let el = routes[i];
if (el.name === route.name) {
return el;
}
}
},
// 获取层级路由
getPath(arr, key, pathAll = []) {
for (let i = 0, len = arr.length; i < len; i++) {
pathAll.push(arr[i]);
if (arr[i].name === key) {
return pathAll;
} else if (arr[i].children && arr[i].children.length >= 1) {
if (this.getPath(arr[i].children, key, pathAll) != false)
return pathAll;
}
pathAll.pop();
}
return false;
},
4、keep-alive 可以缓存组件实例
keep-alive 可以缓存组件实例 是通过 include="['Aaa','Bbb']" 来匹配组件的name是不是 在includes里(区分大小写) 如果是 就缓存下来 如果不是 就不缓存
<keep-alive :include="cachedViews"></keep-alive>
5、keep-alive内的route-view 上可以绑定key
keep-alive内的route-view 上可以绑定key 如果不绑定key那就是一个被缓存的页面 只会缓存一个vue实例 但是如果绑定key key都是唯一的 一个被缓存的页面可以有很多被缓存的vue实例 如果重复进相同的key缓存页面 就会产生一些奇怪的问题
<keep-alive :include="cachedViews">
<router-view :key="key" />
</keep-alive>
6、奇怪问题的解决方案:
当route-view 上可以绑定key值 可以在this.$vnode.parent.componentInstance.cache 里 查到当前所有被缓存的vue实例 我们判断一下 如果当前页面已经有一个缓存了 那就把这个缓存实例给删除 留着最新的缓存实例 就避免了 操作了n次 突然有一次 一进去就有缓存的问题。
//创建一个全局混入的js文件 挂载到main.js下
const mixin = {
// 混入数据
data() {
return {
viewData:{}
};
},
// 混入公用组件
components: {},
// 处理一个页面缓存多个vue实例的问题
beforeRouteLeave(to, from, next) {
try {
const cachedViews = this.$store.state.tagsView.cachedViews;
// const tabFlag = this.$store.state.tabsView.tabFlag;
// const findInCache = cachedViews.find(v => v.includes(from.name))
if (cachedViews.includes(to.name)) {
// 删除缓存
// this.$store.commit("tabsView/SET_CACHEDFLAG", true);
// this.$store.dispatch("tabsView/delTabsView", to.name);
const keepAliveInstance = this.findKeepAlive();
if (!keepAliveInstance) {
return next();
}
// debugger;
const { cache } = keepAliveInstance;
console.log("cache", cache);
const cacheKeys = Object.keys(cache);
let componentsName = to.name.replace(
to.name.charAt(0),
to.name.charAt(0).toLowerCase()
);
let count = 0;
cacheKeys.length &&
cacheKeys.forEach((el) => {
if (el.includes(componentsName)) {
console.log(count, el, componentsName);
count++;
}
});
console.log("count", count);
if (count > 1) {
const delKey = cacheKeys.find((key) => key.includes(componentsName));
delKey && (cache[delKey] = null);
Reflect.deleteProperty(cache, delKey);
}
// this.$destroy() // 调用组件的销毁
}
next();
} catch (error) {
next();
}
},
// 混入过滤器
filters: {},
// 生命周期钩子函数混入
created () {
},
// 混入方法
methods: {
// 查找缓存
findKeepAlive() {
const vnodeParent = this.$vnode.parent;
if (vnodeParent.tag.includes("keep-alive")) {
return vnodeParent.componentInstance;
}
return null;
},
},
};
export default mixin;
注意点:
keep-alive里的 cachedViews中存的路由名必须首字母大写,切与组件的name一致
tips:当前优化
1、只支持三级路由 如果有四级及其以上的路由 需要和产品沟通 在二级或三级路由处做出优化 调整为最多三级路由
2、路由缓存,keep-alive会把缓存的页面给冻结住,切换前后页面不会刷新,如果需要刷新,需要在原来的基础上,用keep-alive的activated、deactivated两个生命周期,进行按需操作。