vue-element-admin icon indicating copy to clipboard operation
vue-element-admin copied to clipboard

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

Open jiaozhiye opened this issue 5 years ago • 18 comments

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

Other relevant information(格外信息)

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

jiaozhiye avatar Jul 17 '19 09:07 jiaozhiye

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

curryhh avatar Jul 19 '19 01:07 curryhh

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

cottengc avatar Aug 01 '19 09:08 cottengc

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

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

qzzm avatar Aug 30 '19 13:08 qzzm

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

ZrealPJ avatar Sep 18 '19 09:09 ZrealPJ

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

liuzihan111111 avatar Oct 12 '19 03:10 liuzihan111111

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

kimmy-wang avatar Dec 13 '19 05:12 kimmy-wang

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

4550155 avatar Mar 10 '20 06:03 4550155

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

leejnull avatar Mar 20 '20 05:03 leejnull

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

具体操作如下

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

trtrtr6 avatar May 22 '20 07:05 trtrtr6

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

具体操作如下

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

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

kailong321200875 avatar May 23 '20 02:05 kailong321200875

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

具体操作如下

  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
    }

trtrtr6 avatar May 25 '20 01:05 trtrtr6

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

具体操作如下

  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
    }

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

kailong321200875 avatar Jun 02 '20 00:06 kailong321200875

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

具体操作如下

  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 avatar Jul 16 '20 02:07 zzc69

@zzc69 import path from 'path'

trtrtr6 avatar Jul 16 '20 02:07 trtrtr6

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

具体操作如下

  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']) 实现方法

TothingWay avatar Oct 03 '20 15:10 TothingWay

能否分享一下这个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;
}

strivezc avatar Dec 07 '20 01:12 strivezc

最好的方式可以将三级或者更多的菜单拍平,菜单的渲染结果默认为后端返回渲染,但是实际生成的路由结果要拍平为二位数组的嵌套结构,改动最少,效果也最好

alexbgw avatar Dec 15 '22 09:12 alexbgw

能否分享一下这个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;
}

应该是浅拷贝component而不是完全不拷贝吧

fahaibing avatar Apr 17 '24 08:04 fahaibing