Vue-element-admin: 在 keep-alive 下嵌套多级 router-view,在 include 属性的约束下,缓存失效

Created on 17 Jul 2019  ·  16Comments  ·  Source: PanJiaChen/vue-element-admin

在 keep-alive 下嵌套多级 router-view,在 include 属性的约束下,缓存失效

Other relevant information(格外信息)

  • Node.js version: v12.3.0
  • vue-element-admin version: v3.11.0

Most helpful comment

经过各种方式的尝试,发现将路由全部转成二级菜单应该是比较不错的处理方式(其它方式多多少少均有一些比较明显的问题)

具体操作如下

  1. 克隆原路由数组(克隆时将component字段除外,也就是component不进行克隆)
  2. 对克隆后的路由数组进行降级处理,动态添加到router中,原路由数组可用来做侧边栏菜单的展示(菜单和路由分离)
  3. 路由降级后,会对面包屑菜单产生影响(展示时中间少了一些层级),可以手动处理还原展示

请教下,能否让我看下你那边的处理代码呢

我把改动的地方都截取出来了,你放到自己的代码中看看

写的比较匆忙,处理方式应该还可以优化,你自己再改改

// 原路由
const asyncRoutes = [...] 

// 降级后的路由
const flatRoutes = getFlatRoutes(deepClone(asyncRoutes, ['component']))

// 几个用到的方法
// 二级以上的菜单降级成二级菜单
const formatRouter = (routes, basePath = '/', list = [], parent) => {
  routes.map(item => {
    item.path = path.resolve(basePath, item.path)
    const meta = item.meta || {}
    if (!meta.parent && parent) {
      meta.parent = parent.path
      item.meta = meta
    }
    if (item.redirect) item.redirect = path.resolve(basePath, item.redirect)
    if (item.children && item.children.length > 0) {
      const arr = formatRouter(item.children, item.path, list, item)
      delete item.children
      list.concat(arr)
    }
    list.push(item)
  })
  return list
}

// 菜单降级
export const getFlatRoutes = (routes) => {
  return routes.map((child) => {
    if (child.children && child.children.length > 0) {
      child.children = formatRouter(child.children, child.path, [], child)
    }
    return child
  })
}
// Breadcrumb/index.vue (面包屑处理)

...
 // let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
 let matched = this.getBreadcrumbRoutes().filter(item => item.meta && item.meta.title)
....

   getBreadcrumbRoutes () {
      let currentRoutes = {}
      const first = this.$route.matched[0]
      let last = this.$route.matched[this.$route.matched.length - 1]
      const matched = []

      // 倒序遍历有meta.parent标识的路由
      for (let i = this.$route.matched.length - 1; i >= 0; i--) {
        const match = this.$route.matched[i]
        const meta = match.meta || {}
        matched.unshift(match)
        if (meta.parent) {
          last = match
          break
        }
      }

      // 填充降级后缺失的各级路由
      this.flatRoutes.some(item => {
        if (item.path === first.path) {
          currentRoutes = item.children
          this.getParentRoute(currentRoutes, last, matched)
          return true
        }
      })
      matched.unshift(first)
      return matched
    },
    getParentRoute (currentRoutes, last, matched = []) {
      const meta = last.meta || {}
      currentRoutes.forEach((item) => {
        if (item.path === meta.parent) {
          matched.unshift(item)
          this.getParentRoute(currentRoutes, item, matched)
        }
      })
      return matched
    }

All 16 comments

路由name跟组件name要一样是不是这个问题或者没设置

去掉include虽然可以缓存了,但是有很多问题,所有在tagviews上的组件会重新渲染

路由name跟组件name要一样是不是这个问题或者没设置

我也遇到和楼主一样的问题,打印过路由name和组件name一样的,同样在多级下失效,若楼主解决麻烦指点下哦,谢谢

我也遇到相同的问题,多级组件下,未缓存父级组件,导致子组件未缓存。
感觉应该要在父级的那个位置加

我把父级组件的name直接写进了cacheViews数组里面,虽然可以缓存子组件,但是导致子组件关闭后还一直在缓存,不知道该怎么办

我也遇到了相同的问题,去掉include就可以了

我是也是遇到这个问题,有解决方案没?

root-view嵌套导致的。正常一级菜单下只有二级菜单是没问题的,但是有三级菜单就会出现

经过各种方式的尝试,发现将路由全部转成二级菜单应该是比较不错的处理方式(其它方式多多少少均有一些比较明显的问题)

具体操作如下

  1. 克隆原路由数组(克隆时将component字段除外,也就是component不进行克隆)
  2. 对克隆后的路由数组进行降级处理,动态添加到router中,原路由数组可用来做侧边栏菜单的展示(菜单和路由分离)
  3. 路由降级后,会对面包屑菜单产生影响(展示时中间少了一些层级),可以手动处理还原展示

经过各种方式的尝试,发现将路由全部转成二级菜单应该是比较不错的处理方式(其它方式多多少少均有一些比较明显的问题)

具体操作如下

  1. 克隆原路由数组(克隆时将component字段除外,也就是component不进行克隆)
  2. 对克隆后的路由数组进行降级处理,动态添加到router中,原路由数组可用来做侧边栏菜单的展示(菜单和路由分离)
  3. 路由降级后,会对面包屑菜单产生影响(展示时中间少了一些层级),可以手动处理还原展示

请教下,能否让我看下你那边的处理代码呢

经过各种方式的尝试,发现将路由全部转成二级菜单应该是比较不错的处理方式(其它方式多多少少均有一些比较明显的问题)

具体操作如下

  1. 克隆原路由数组(克隆时将component字段除外,也就是component不进行克隆)
  2. 对克隆后的路由数组进行降级处理,动态添加到router中,原路由数组可用来做侧边栏菜单的展示(菜单和路由分离)
  3. 路由降级后,会对面包屑菜单产生影响(展示时中间少了一些层级),可以手动处理还原展示

请教下,能否让我看下你那边的处理代码呢

我把改动的地方都截取出来了,你放到自己的代码中看看

写的比较匆忙,处理方式应该还可以优化,你自己再改改

// 原路由
const asyncRoutes = [...] 

// 降级后的路由
const flatRoutes = getFlatRoutes(deepClone(asyncRoutes, ['component']))

// 几个用到的方法
// 二级以上的菜单降级成二级菜单
const formatRouter = (routes, basePath = '/', list = [], parent) => {
  routes.map(item => {
    item.path = path.resolve(basePath, item.path)
    const meta = item.meta || {}
    if (!meta.parent && parent) {
      meta.parent = parent.path
      item.meta = meta
    }
    if (item.redirect) item.redirect = path.resolve(basePath, item.redirect)
    if (item.children && item.children.length > 0) {
      const arr = formatRouter(item.children, item.path, list, item)
      delete item.children
      list.concat(arr)
    }
    list.push(item)
  })
  return list
}

// 菜单降级
export const getFlatRoutes = (routes) => {
  return routes.map((child) => {
    if (child.children && child.children.length > 0) {
      child.children = formatRouter(child.children, child.path, [], child)
    }
    return child
  })
}
// Breadcrumb/index.vue (面包屑处理)

...
 // let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
 let matched = this.getBreadcrumbRoutes().filter(item => item.meta && item.meta.title)
....

   getBreadcrumbRoutes () {
      let currentRoutes = {}
      const first = this.$route.matched[0]
      let last = this.$route.matched[this.$route.matched.length - 1]
      const matched = []

      // 倒序遍历有meta.parent标识的路由
      for (let i = this.$route.matched.length - 1; i >= 0; i--) {
        const match = this.$route.matched[i]
        const meta = match.meta || {}
        matched.unshift(match)
        if (meta.parent) {
          last = match
          break
        }
      }

      // 填充降级后缺失的各级路由
      this.flatRoutes.some(item => {
        if (item.path === first.path) {
          currentRoutes = item.children
          this.getParentRoute(currentRoutes, last, matched)
          return true
        }
      })
      matched.unshift(first)
      return matched
    },
    getParentRoute (currentRoutes, last, matched = []) {
      const meta = last.meta || {}
      currentRoutes.forEach((item) => {
        if (item.path === meta.parent) {
          matched.unshift(item)
          this.getParentRoute(currentRoutes, item, matched)
        }
      })
      return matched
    }

经过各种方式的尝试,发现将路由全部转成二级菜单应该是比较不错的处理方式(其它方式多多少少均有一些比较明显的问题)

具体操作如下

  1. 克隆原路由数组(克隆时将component字段除外,也就是component不进行克隆)
  2. 对克隆后的路由数组进行降级处理,动态添加到router中,原路由数组可用来做侧边栏菜单的展示(菜单和路由分离)
  3. 路由降级后,会对面包屑菜单产生影响(展示时中间少了一些层级),可以手动处理还原展示

请教下,能否让我看下你那边的处理代码呢

我把改动的地方都截取出来了,你放到自己的代码中看看

写的比较匆忙,处理方式应该还可以优化,你自己再改改

// 原路由
const asyncRoutes = [...] 

// 降级后的路由
const flatRoutes = getFlatRoutes(deepClone(asyncRoutes, ['component']))

// 几个用到的方法
// 二级以上的菜单降级成二级菜单
const formatRouter = (routes, basePath = '/', list = [], parent) => {
  routes.map(item => {
    item.path = path.resolve(basePath, item.path)
    const meta = item.meta || {}
    if (!meta.parent && parent) {
      meta.parent = parent.path
      item.meta = meta
    }
    if (item.redirect) item.redirect = path.resolve(basePath, item.redirect)
    if (item.children && item.children.length > 0) {
      const arr = formatRouter(item.children, item.path, list, item)
      delete item.children
      list.concat(arr)
    }
    list.push(item)
  })
  return list
}

// 菜单降级
export const getFlatRoutes = (routes) => {
  return routes.map((child) => {
    if (child.children && child.children.length > 0) {
      child.children = formatRouter(child.children, child.path, [], child)
    }
    return child
  })
}
// Breadcrumb/index.vue (面包屑处理)

...
 // let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
 let matched = this.getBreadcrumbRoutes().filter(item => item.meta && item.meta.title)
....

   getBreadcrumbRoutes () {
      let currentRoutes = {}
      const first = this.$route.matched[0]
      let last = this.$route.matched[this.$route.matched.length - 1]
      const matched = []

      // 倒序遍历有meta.parent标识的路由
      for (let i = this.$route.matched.length - 1; i >= 0; i--) {
        const match = this.$route.matched[i]
        const meta = match.meta || {}
        matched.unshift(match)
        if (meta.parent) {
          last = match
          break
        }
      }

      // 填充降级后缺失的各级路由
      this.flatRoutes.some(item => {
        if (item.path === first.path) {
          currentRoutes = item.children
          this.getParentRoute(currentRoutes, last, matched)
          return true
        }
      })
      matched.unshift(first)
      return matched
    },
    getParentRoute (currentRoutes, last, matched = []) {
      const meta = last.meta || {}
      currentRoutes.forEach((item) => {
        if (item.path === meta.parent) {
          matched.unshift(item)
          this.getParentRoute(currentRoutes, item, matched)
        }
      })
      return matched
    }

好的,谢谢,我这里研究一下看看。

经过各种方式的尝试,发现将路由全部转成二级菜单应该是比较不错的处理方式(其它方式多多少少均有一些比较明显的问题)

具体操作如下

  1. 克隆原路由数组(克隆时将component字段除外,也就是component不进行克隆)
  2. 对克隆后的路由数组进行降级处理,动态添加到router中,原路由数组可用来做侧边栏菜单的展示(菜单和路由分离)
  3. 路由降级后,会对面包屑菜单产生影响(展示时中间少了一些层级),可以手动处理还原展示

请教下,能否让我看下你那边的处理代码呢

我把改动的地方都截取出来了,你放到自己的代码中看看

写的比较匆忙,处理方式应该还可以优化,你自己再改改

// 原路由
const asyncRoutes = [...] 

// 降级后的路由
const flatRoutes = getFlatRoutes(deepClone(asyncRoutes, ['component']))

// 几个用到的方法
// 二级以上的菜单降级成二级菜单
const formatRouter = (routes, basePath = '/', list = [], parent) => {
  routes.map(item => {
    item.path = path.resolve(basePath, item.path)
    const meta = item.meta || {}
    if (!meta.parent && parent) {
      meta.parent = parent.path
      item.meta = meta
    }
    if (item.redirect) item.redirect = path.resolve(basePath, item.redirect)
    if (item.children && item.children.length > 0) {
      const arr = formatRouter(item.children, item.path, list, item)
      delete item.children
      list.concat(arr)
    }
    list.push(item)
  })
  return list
}

// 菜单降级
export const getFlatRoutes = (routes) => {
  return routes.map((child) => {
    if (child.children && child.children.length > 0) {
      child.children = formatRouter(child.children, child.path, [], child)
    }
    return child
  })
}
// Breadcrumb/index.vue (面包屑处理)

...
 // let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
 let matched = this.getBreadcrumbRoutes().filter(item => item.meta && item.meta.title)
....

   getBreadcrumbRoutes () {
      let currentRoutes = {}
      const first = this.$route.matched[0]
      let last = this.$route.matched[this.$route.matched.length - 1]
      const matched = []

      // 倒序遍历有meta.parent标识的路由
      for (let i = this.$route.matched.length - 1; i >= 0; i--) {
        const match = this.$route.matched[i]
        const meta = match.meta || {}
        matched.unshift(match)
        if (meta.parent) {
          last = match
          break
        }
      }

      // 填充降级后缺失的各级路由
      this.flatRoutes.some(item => {
        if (item.path === first.path) {
          currentRoutes = item.children
          this.getParentRoute(currentRoutes, last, matched)
          return true
        }
      })
      matched.unshift(first)
      return matched
    },
    getParentRoute (currentRoutes, last, matched = []) {
      const meta = last.meta || {}
      currentRoutes.forEach((item) => {
        if (item.path === meta.parent) {
          matched.unshift(item)
          this.getParentRoute(currentRoutes, item, matched)
        }
      })
      return matched
    }

好的,谢谢,我这里研究一下看看。

path.resolve(basePath, item.path) 这path是哪的。。。

@zzc69 import path from 'path'

经过各种方式的尝试,发现将路由全部转成二级菜单应该是比较不错的处理方式(其它方式多多少少均有一些比较明显的问题)

具体操作如下

  1. 克隆原路由数组(克隆时将component字段除外,也就是component不进行克隆)
  2. 对克隆后的路由数组进行降级处理,动态添加到router中,原路由数组可用来做侧边栏菜单的展示(菜单和路由分离)
  3. 路由降级后,会对面包屑菜单产生影响(展示时中间少了一些层级),可以手动处理还原展示

请教下,能否让我看下你那边的处理代码呢

我把改动的地方都截取出来了,你放到自己的代码中看看

写的比较匆忙,处理方式应该还可以优化,你自己再改改

// 原路由
const asyncRoutes = [...] 

// 降级后的路由
const flatRoutes = getFlatRoutes(deepClone(asyncRoutes, ['component']))

// 几个用到的方法
// 二级以上的菜单降级成二级菜单
const formatRouter = (routes, basePath = '/', list = [], parent) => {
  routes.map(item => {
    item.path = path.resolve(basePath, item.path)
    const meta = item.meta || {}
    if (!meta.parent && parent) {
      meta.parent = parent.path
      item.meta = meta
    }
    if (item.redirect) item.redirect = path.resolve(basePath, item.redirect)
    if (item.children && item.children.length > 0) {
      const arr = formatRouter(item.children, item.path, list, item)
      delete item.children
      list.concat(arr)
    }
    list.push(item)
  })
  return list
}

// 菜单降级
export const getFlatRoutes = (routes) => {
  return routes.map((child) => {
    if (child.children && child.children.length > 0) {
      child.children = formatRouter(child.children, child.path, [], child)
    }
    return child
  })
}
// Breadcrumb/index.vue (面包屑处理)

...
 // let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
 let matched = this.getBreadcrumbRoutes().filter(item => item.meta && item.meta.title)
....

   getBreadcrumbRoutes () {
      let currentRoutes = {}
      const first = this.$route.matched[0]
      let last = this.$route.matched[this.$route.matched.length - 1]
      const matched = []

      // 倒序遍历有meta.parent标识的路由
      for (let i = this.$route.matched.length - 1; i >= 0; i--) {
        const match = this.$route.matched[i]
        const meta = match.meta || {}
        matched.unshift(match)
        if (meta.parent) {
          last = match
          break
        }
      }

      // 填充降级后缺失的各级路由
      this.flatRoutes.some(item => {
        if (item.path === first.path) {
          currentRoutes = item.children
          this.getParentRoute(currentRoutes, last, matched)
          return true
        }
      })
      matched.unshift(first)
      return matched
    },
    getParentRoute (currentRoutes, last, matched = []) {
      const meta = last.meta || {}
      currentRoutes.forEach((item) => {
        if (item.path === meta.parent) {
          matched.unshift(item)
          this.getParentRoute(currentRoutes, item, matched)
        }
      })
      return matched
    }

能否分享一下这个deepClone(asyncRoutes, ['component']) 实现方法

能否分享一下这个deepClone(asyncRoutes, ['component']) 实现方法

作者的意思就是把component这个key复制时忽略,改写一下deepClone
/**

  • @param {Object} source
  • @param {Array} ignore
  • @returns {Object}
    */
export function deepClone(source,ignore) {
  if (!source && typeof source !== 'object') {
    throw new Error('error arguments', 'deepClone');
  }
  const targetObj = source.constructor === Array ? [] : {};
  Object.keys(source).forEach(keys => {
    if (!ignore.includes(keys)) {
      if (source[keys] && typeof source[keys] === 'object') {
        targetObj[keys] = deepClone(source[keys],ignore);
      } else {
        targetObj[keys] = source[keys];
      }
    }
  });
  return targetObj;
}
Was this page helpful?
0 / 5 - 0 ratings