解决vue-router在3级路由keep-alive后组件加载多次方法

发布时间 2023-07-11 18:45:35作者: 猪猪侠之歌

问题

连续两天遇到keepalive问题,第一个问题是三级路由嵌套router-view没法缓存问题,第二个问题是使用keepalive导致组件渲染两次问题,深坑。。。
官方bug 
 

解决思路

引入routerHelper,createRouter时三级路由提升为两层

import { deepClone } from '@/utils';
import { createRouter, createWebHashHistory } from 'vue-router';

/**
 * 将多级路由转换为 2 级路由
 */
export function flatMultiLevelRoutes(routeModules) {
  const modules = deepClone(routeModules);

  for (let index = 0; index < modules.length; index++) {
    const routeModule = modules[index];
    // 判断级别是否 多级 路由
    if (!isMultipleRoute(routeModule)) {
      // 声明终止当前循环, 即跳过此次循环,进行下一轮
      continue;
    }
    // 路由等级提升
    promoteRouteLevel(routeModule);
  }
  return modules;
}

// 路由等级提升
function promoteRouteLevel(routeModule) {
  // Use vue-router to splice menus
  // 使用vue-router拼接菜单
  // createRouter 创建一个可以被 Vue 应用程序使用的路由实例
  let router = createRouter({
    routes: [routeModule],
    history: createWebHashHistory(),
  });
  // getRoutes: 获取所有 路由记录的完整列表。
  const routes = router.getRoutes();
  // 将所有子路由添加到二级路由
  addToChildren(routes, routeModule.children || [], routeModule);
  router = null;

  // 对传入的item对象的children进行删除
  routeModule.children = routeModule.children?.map((item) => {
    item.children = undefined;
    return item;
  });
}

// 将所有子路由添加到二级路由
function addToChildren(routes, children, routeModule) {
  for (let index = 0; index < children.length; index++) {
    const child = children[index];
    const route = routes.find((item) => item.name === child.name);
    if (!route) {
      continue;
    }
    routeModule.children = routeModule.children || [];
    if (!routeModule.children.find((item) => item.name === route.name)) {
      routeModule.children?.push(route);
    }
    if (child.children?.length) {
      addToChildren(routes, child.children, routeModule);
    }
  }
}

// 判断级别是否超过2级
function isMultipleRoute(routeModule) {
  // Reflect.has 与 in 操作符 相同, 用于检查一个对象(包括它原型链上)是否拥有某个属性
  if (
    !routeModule ||
    !Reflect.has(routeModule, 'children') ||
    !routeModule.children?.length
  ) {
    return false;
  }

  const children = routeModule.children;

  let flag = false;
  for (let index = 0; index < children.length; index++) {
    const child = children[index];
    if (child.children?.length) {
      flag = true;
      break;
    }
  }
  return flag;
}

// 使用
const router = createRouter({
  history: createWebHashHistory(),
  routes: [...flatMultiLevelRoutes(constantRoutes)], // 菜单用constantRoutes来循环生成,而createRouter用调整过的路由
});