vue-element-admin/litemall后端,超过两级嵌套路由无法缓存的问题

本文主要针对的是litemall后端,vue-element-admin只需稍作修改应该也可以适用。路由扁平的思路主要来源于https://blog.csdn.net/weixin_40119256/article/details/111475571,另外解决面包屑显示问题,此文章作记录以供有需要的同行参考。

keep-alive 用于缓存不活跃的组件实例,避免重复渲染和重新创建组件实例。然而,keep-alive 只能缓存其内部的最外层组件,对于嵌套的子组件,keep-alive 无法直接缓存。vue router的第一层是Layout component,如果二级路由是页面则keep-alive自然可以缓存。但是如果嵌套路由超过了两级,则必然会在页面上层至少套上了一层以上的component,keep-alive无法直接缓存,这就是本文要解决的问题。

把路由变成二级的扁平化

  • 更新文件./src/store/modules/permission.js

添加获取扁平化路由表的方法。跟参考文章不一样的地方是,如果遇到条目的path第一个字符是'/',则path从当前开始。如当前条目的path是/me,即使父的path是/parent,当前的path会变成/me。

另外修复源代码的bug,castToFlatRoute(item.children, ’‘)修改为castToFlatRoute(item.children, item.path)。如果存在一个二级菜单,生成的path会缺失父级的path。同时加上props的传递,不然原来通过props传递的参数丢失。

/**
 * 生成扁平化机构路由(仅两级结构)
 * @param {允许访问的路由Tree} accessRoutes
 * 路由基本机构:
 * {
 *   name: String,
 *   path: String,
 *   component: Component,
 *   redirect: String,
 *   children: [
 *   ]
 * }
 */
function generateFlatRoutes(accessRoutes) {
  const flatRoutes = []

  for (const item of accessRoutes) {
    let childrenFflatRoutes = []
    if (item.children && item.children.length > 0) {
      childrenFflatRoutes = castToFlatRoute(item.children, item.path)
    }

    // 一级路由是布局路由,需要处理的只是其子路由数据
    flatRoutes.push({
      name: item.name,
      path: item.path,
      component: item.component,
      props: item.props,
      redirect: item.redirect,
      meta: item.meta,
      children: childrenFflatRoutes
    })
  }

  return flatRoutes
}
/**
   * 将子路由转换为扁平化路由数组(仅一级)
   * @param {待转换的子路由数组} routes
   * @param {父级路由路径} parentPath
   */
function castToFlatRoute(routes, parentPath, flatRoutes = []) {
  for (const item of routes) {
    let newPath = parentPath + '/' + item.path
    if (item.path.substring(0, 1) === '/') {
      // set path start from currentpath, if this item's path start with '/'
      newPath = item.path
    }

    if (item.children && item.children.length > 0) {
      if (item.redirect && item.redirect !== 'noredirect') {
        flatRoutes.push({
          name: item.name,
          path: newPath,
          redirect: item.redirect,
          meta: item.meta
        })
      }
      castToFlatRoute(item.children, newPath, flatRoutes)
    } else {
      flatRoutes.push({
        name: item.name,
        path: newPath,
        component: item.component,
      props: item.props,
        redirect: item.redirect,
        meta: item.meta
      })
    }
  }

  return flatRoutes
}

更新GenerateRoutes()方法返回扁平后的路由表。注意不要动原有的这句”commit('SET_ROUTERS', accessedRouters)“。

const flatRoutes = generateFlatRoutes(accessedRouters)
resolve(flatRoutes)

 

更新Router

  • 更新文件./src/permission.js

用router.addRoutes(flatRoutes)替换router.addRoutes(store.getters.addRouters)。

store
  .dispatch('GetUserInfo', getUserToken())
  .then(res => {
    // 拉取user_info
    const perms = store.getters.perms // note: perms must be a array! such as: ['GET /aaa','POST /bbb']
    store
      .dispatch('GenerateRoutes', {
        perms
      })
      .then((flatRoutes) => {
        // flat routes to fix the issue of keep alive cannot cache nested component
        router.addRoutes(flatRoutes)

        // 根据perms权限生成可访问的路由表
        // router.addRoutes(store.getters.addRouters) // 动态添加可访问路由表
        next({
          ...to,
          replace: true
        }) // hack方法 确保addRoutes已完成 ,set the replace: true so the navigation will not leave a history record
      })
  })
  .catch(err => {
    store.dispatch('FedLogOut').then(() => {
      Message.error(err || 'Verification failed, please login again')
      next({
        path: '/'
      })
    })
  })

 

解决面包屑显示问题

  • 更新文件./src/store/modules/permission.js

在原有的route上添加matched信息。

/**
 * add matched for breadcrumb display
 * @param routes routes list
 * @returns routers
 */
function addBreadcrumb(routes, matched = []) {
  const res = []
  routes.forEach(route => {
    const tmp = {
      ...route
    }
    const tmpMatched = [
      ...matched
    ]
    tmpMatched.push({
      meta: (tmp.meta ? tmp.meta : undefined),
      redirect: tmp.redirect,
      path: (tmp.path ? tmp.path : '/') })

    if (tmp.children) {
      tmp.children = addBreadcrumb(tmp.children, tmpMatched)
      if (tmp.children && tmp.children.length > 0) {
        tmp.matched = tmpMatched
        res.push(tmp)
      }
    } else {
      tmp.matched = tmpMatched
      res.push(tmp)
    }
  })
  return res
}

更新GenerateRoutes()方法在保存之前添加matched信息。

if (perms.includes('*')) {
  accessedRouters = addBreadcrumb(asyncRouterMap)
} else {
  accessedRouters = addBreadcrumb(filterAsyncRouter(asyncRouterMap, perms))
}
  •  更新文件./src/components/Breadcrumb/index

添加getMatchRoute()用于查找匹配的路由。

getMatchRoute(routers, targetName) {
  for (let k = 0; k < routers.length; k++) {
    const route = routers[k]
    if (route.children && route.children.length > 0) {
      const matched = this.getMatchRoute(route.children, targetName)
      if (matched !== undefined) {
        return matched
      }
    } else if (route.name === targetName) {
      return route
    }
  }
},

添加属性fullRoute用于保存包含面包屑信息的匹配的路由条目。更新getBreadcrumb()根据匹配的路由生成面包屑。

getBreadcrumb() {
  this.fullRoute = this.getMatchRoute(store.getters.addRouters, this.$route.name)
  if (!this.fullRoute) {
    this.fullRoute = this.$route
  }

  // only show routes with meta.title
  let matched = this.fullRoute.matched.filter(
    item => item.meta && item.meta.title
  )
  const first = matched[0]
  if (!this.isDashboard(first)) {
    matched = [{ path: '/home', meta: { title: 'Home' }}].concat(matched)
  }
  this.levelList = matched.filter(
    item => item.meta && item.meta.title && item.meta.breadcrumb !== false
  )
},

更新pathCompile(),生成链接。

pathCompile(path) {
  // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
  const { params } = this.fullRoute
  var toPath = pathToRegexp.compile(path)
  return toPath(params)
},

 

注意:如果要启用页面缓存,必须保证路由的name跟component的name一模一样。

 

参考:

https://blog.csdn.net/weixin_40119256/article/details/111475571

 

转载请注明出处:https://www.cnblogs.com/keitsi/p/17969846

posted @ 2024-01-17 17:18  keitsi  阅读(162)  评论(0编辑  收藏  举报