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

三级菜单缓存问题解决方案

Open leekbillow opened this issue 2 years ago • 17 comments

2022/07/08更新了写法(往下翻翻),原内容已过时

以下为原内容

2018年就有相关提问,最后似乎作者似乎也没有把这个问题解决?好像也不怎么更新维护了 最近折腾这个问题提供一个方案: 1.src/store/modules/tagsView.js中修改ADD_CACHED_VIEW方法,将二级菜单加入缓存

ADD_CACHED_VIEW: (state, view) => {
    if (state.cachedViews.includes(view.name)) return;
    const { matched, meta, name } = view;
    if (!meta.noCache) {
      if (matched.length > 2) {
        //路由层级大于2时,将父级也加入cachedViews
        for (let i = 1; i < matched.length; i++) {
          if (!state.cachedViews.includes(matched[i].name)) {
            state.cachedViews.push(matched[i].name);
          }
        }
      } else {
        state.cachedViews.push(name);
      }
    }
  }

2.要缓存三级菜单的父级vue自己包一层<keep-alive></keep-alive>,确保include数组包含三级菜单name

<template>
  <div class="page-padding">
    <keep-alive :include="cachedViews">
      <router-view :key="key"></router-view>
    </keep-alive>
  </div>
</template>

<script>
export default {
  name: "记得要填写name",
  computed: {
    cachedViews() {
      return this.$store.state.tagsView.cachedViews;
    },
    key() {
      return this.$route.path;
    },
  },
};
</script>

leekbillow avatar Jul 09 '21 08:07 leekbillow

大佬,我照着你这样改但还是不起作用,而且多个路由公用一个组件页面name怎么弄?

Leslie1900 avatar Jul 12 '21 02:07 Leslie1900

大佬,我照着你这样改但还是不起作用,而且多个路由公用一个组件页面name怎么弄?

我也不知道你是什么情况,检查一下菜单缓存要开启,各个name也要对应上才能缓存成功; 缓存也是填页面的name,跟组件没关系啊;

leekbillow avatar Jul 12 '21 02:07 leekbillow

大佬,我照着你这样改但还是不起作用,而且多个路由公用一个组件页面name怎么弄?

我也不知道你是什么情况,检查一下菜单缓存要开启,各个name也要对应上才能缓存成功; 缓存也是填页面的name,跟组件没关系啊;

你的意思是多个路由对应同一页面?那也还是那样填啊,不过我没有试过效果如何; 【2】 的name填的是二级的name,不是三级name

leekbillow avatar Jul 12 '21 03:07 leekbillow

多级路由嵌套缓存的唯一解决方案 #3436

hooray avatar Jul 16 '21 01:07 hooray

多级路由嵌套缓存的唯一解决方案 #3436

太麻烦了,还要处理路由和面包屑; 缓存二级会导致缓存所有该二级下的所有三级,但我的处理方式是二级vue文件自己套一层keep-alive,传入include和key就可以将三级菜单缓存区分开,这种方法并不需要改路由文件也不需要改面包屑;

leekbillow avatar Jul 16 '21 01:07 leekbillow

多级路由嵌套缓存的唯一解决方案 #3436

太麻烦了,还要处理路由和面包屑; 缓存二级会导致缓存所有该二级下的所有三级,但我的处理方式是二级vue文件自己套一层keep-alive,传入include和key就可以将三级菜单缓存区分开,这种方法并不需要改路由文件也不需要改面包屑;

这种方式的确比他们那种还要处理的简单

linwenfeng122 avatar Jul 31 '21 10:07 linwenfeng122

用同样的方法处理4级路由嵌套,没有效果

wanqianjin avatar Dec 14 '21 05:12 wanqianjin

有2点缺陷,1.在3级嵌套的情况下,2级路由的缓存始终无法删除,缺少对应的删除逻辑,也可能与我2级路由设置了重定向有关;2. 在keep-alive存在动态key属性的情况下,会导致多余的缓存页面出现,这些都可以在vue-tool中观察到; 1的解决方法是加个计数器,同时添加对应的删除逻辑,在计数器为0时删除2级缓存,2的解决方法就是去除key属性

YOOYY avatar Jun 10 '22 07:06 YOOYY

还有个bug,三级嵌套路由缓存情况下,2级路由的切换会出现三级页面错乱的情况,而且没找到啥好的解决办法,最后还是改成了 拉平嵌套路由那种方法

YOOYY avatar Jun 21 '22 09:06 YOOYY

@YOOYY 你说得没错

  • 雀食少了删除二级缓存的逻辑,三级全关的情况下二级缓存没移除
  • 二级目录的vue文件里router带key<router-view :key="key"/>雀食也会导致额外的页面生成,因为之前我一直做的页面created里面都只有缓存的请求,并没有发现这个问题

leekbillow avatar Jul 08 '22 03:07 leekbillow

还有个bug,三级嵌套路由缓存情况下,2级路由的切换会出现三级页面错乱的情况,而且没找到啥好的解决办法,最后还是改成了 拉平嵌套路由那种方法

移除key的话会导致页面没有重新加载,依次打开页面1-1、1-2、2-1,从1-2切换到2-1后再切回1-1,此时只是把2二级路由切回1,但下面的三级路由没有更新,保持了之前的状态(1-2),所以造成了这个错乱的情况,解决方法就是在切换二级路由时调用$forceUpdate刷新,就可以切回正确匹配的页面了

leekbillow avatar Jul 08 '22 03:07 leekbillow

该issue写在一年前,没想到刚好就跟去年写的时候差一天,正牌作者不出手只能自己提点拙见尝试解决。 之前的解决方法有问题:

  1. 会导致多余的页面生成
  2. 三级页面全部关掉后二级也始终缓存着一个二级页面

尝试再更新一版解决方法: 1.在tagsView.js添加缓存逻辑中,将上级菜单加入缓存

ADD_CACHED_VIEW: (state, view) => {
    if (state.cachedViews.includes(view.name)) return;
    const { matched, meta, name } = view;
    if (!meta.noCache) {
      if (matched.length > 2) {
        //路由层级大于2时,将父级也加入cachedViews
        for (let i = 1; i < matched.length; i++) {
          if (!state.cachedViews.includes(matched[i].name)) {
            state.cachedViews.push(matched[i].name);
          }
        }
      } else {
        state.cachedViews.push(name);
      }
    }
  }

2.在tagsView.js删除逻辑中,判断二级下没有缓存的三级时,将二级缓存也移除

DEL_CACHED_VIEW: (state, view) => {
    const index = state.cachedViews.indexOf(view.name);
    index > -1 && state.cachedViews.splice(index, 1);
    if (view.matched.length > 2) {
      //路由层级大于2,检查并移除没有子页面缓存的上级路由
      view.matched.every(() => {
        const originLength = state.cachedViews.length;
        const inuseCacheNames = state.visitedViews.reduce(
          (inuseCacheNames, { matched }) => inuseCacheNames.concat(matched.map(({ name }) => name)),
          []
        );
        state.cachedViews = state.cachedViews.filter((E) => inuseCacheNames.includes(E));
        return originLength === state.cachedViews.length;
      });
    }
  }

3.AppMain.vue中,移除<router-view>的key

<template>
  <section class="app-main">
    <transition name="fade-transform" mode="out-in">
      <keep-alive :include="cachedViews">
        <router-view />
      </keep-alive>
    </transition>
  </section>
</template>

4.二级vue页面做一些改变:

  • AppMain.vue移除了key后,切换三级页面会丢失动画,要自己套一层<transition>
  • 为了解决切到其它二级路由所属页面后切回来三级路由错乱的问题,要在keep-alive激活后调用$forceUpdate
  • 如果页面不符时,调用$forceUpdate刷新会多一段额外的过渡动画,所以要自行控制过渡动画(示例中使用了isTransition变量,可自行调整逻辑)
<template>
  <transition :name="isTransition ? 'fade-transform' : ''" mode="out-in" @before-leave="1">
    <keep-alive :include="cachedViews">
      <router-view />
    </keep-alive>
  </transition>
</template>

<script>
export default {
  name: "记得填写name",
  data() {
    return { isTransition: true };
  },
  computed: {
    cachedViews() {
      return this.$store.state.tagsView.cachedViews;
    },
  },
  activated() {
    this.$forceUpdate();
    this.$nextTick(() => (this.isTransition = true));
  },
  deactivated() {
    this.isTransition = false;
  },
};
</script>

leekbillow avatar Jul 08 '22 07:07 leekbillow

该issue写在一年前,没想到刚好就跟去年写的时候差一天,正牌作者不出手只能自己提点拙见尝试解决。 之前的解决方法有问题:

  1. 会导致多余的页面生成
  2. 三级页面全部关掉后二级也始终缓存着一个二级页面

尝试再更新一版解决方法: 1.在tagsView.js添加缓存逻辑中,将上级菜单加入缓存

ADD_CACHED_VIEW: (state, view) => {
    if (state.cachedViews.includes(view.name)) return;
    const { matched, meta, name } = view;
    if (!meta.noCache) {
      if (matched.length > 2) {
        //路由层级大于2时,将父级也加入cachedViews
        for (let i = 1; i < matched.length; i++) {
          if (!state.cachedViews.includes(matched[i].name)) {
            state.cachedViews.push(matched[i].name);
          }
        }
      } else {
        state.cachedViews.push(name);
      }
    }
  }

2.在tagsView.js删除逻辑中,判断二级下没有缓存的三级时,将二级缓存也移除

DEL_CACHED_VIEW: (state, view) => {
    const index = state.cachedViews.indexOf(view.name);
    index > -1 && state.cachedViews.splice(index, 1);
    if (view.matched.length > 2) {
      //路由层级大于2,检查并移除没有子页面缓存的上级路由
      view.matched.every(() => {
        const originLength = state.cachedViews.length;
        const inuseCacheNames = state.visitedViews.reduce(
          (inuseCacheNames, { matched }) => inuseCacheNames.concat(matched.map(({ name }) => name)),
          []
        );
        state.cachedViews = state.cachedViews.filter((E) => inuseCacheNames.includes(E));
        return originLength === state.cachedViews.length;
      });
    }
  }

3.AppMain.vue中,移除<router-view>的key

<template>
  <section class="app-main">
    <transition name="fade-transform" mode="out-in">
      <keep-alive :include="cachedViews">
        <router-view />
      </keep-alive>
    </transition>
  </section>
</template>

4.二级vue页面做一些改变:

  • AppMain.vue移除了key后,切换三级页面会丢失动画,要自己套一层<transition>
  • 为了解决切到其它二级路由所属页面后切回来三级路由错乱的问题,要在keep-alive激活后调用$forceUpdate
  • 如果页面不符时,调用$forceUpdate刷新会多一段额外的过渡动画,所以要自行控制过渡动画(示例中使用了isTransition变量,可自行调整逻辑)
<template>
  <transition :name="isTransition ? 'fade-transform' : ''" mode="out-in" @before-leave="1">
    <keep-alive :include="cachedViews">
      <router-view />
    </keep-alive>
  </transition>
</template>

<script>
export default {
  name: "记得填写name",
  data() {
    return { isTransition: true };
  },
  computed: {
    cachedViews() {
      return this.$store.state.tagsView.cachedViews;
    },
  },
  activated() {
    this.$forceUpdate();
    this.$nextTick(() => (this.isTransition = true));
  },
  deactivated() {
    this.isTransition = false;
  },
};
</script>

问题1: 二级vue页面是用的公共页面,只有一个name, 那岂不是每个二级路由都要新建一个路由页面 问题2: 怎么解决路由层级不确定的情况, 比如4层5层

s12he avatar Jul 21 '22 07:07 s12he

该issue写在一年前,没想到刚好就跟去年写的时候差一天,正牌作者不出手只能自己提点拙见尝试解决。 之前的解决方法有问题:

  1. 会导致多余的页面生成
  2. 三级页面全部关掉后二级也始终缓存着一个二级页面

尝试再更新一版解决方法: 1.在tagsView.js添加缓存逻辑中,将上级菜单加入缓存

ADD_CACHED_VIEW: (state, view) => {
    if (state.cachedViews.includes(view.name)) return;
    const { matched, meta, name } = view;
    if (!meta.noCache) {
      if (matched.length > 2) {
        //路由层级大于2时,将父级也加入cachedViews
        for (let i = 1; i < matched.length; i++) {
          if (!state.cachedViews.includes(matched[i].name)) {
            state.cachedViews.push(matched[i].name);
          }
        }
      } else {
        state.cachedViews.push(name);
      }
    }
  }

2.在tagsView.js删除逻辑中,判断二级下没有缓存的三级时,将二级缓存也移除

DEL_CACHED_VIEW: (state, view) => {
    const index = state.cachedViews.indexOf(view.name);
    index > -1 && state.cachedViews.splice(index, 1);
    if (view.matched.length > 2) {
      //路由层级大于2,检查并移除没有子页面缓存的上级路由
      view.matched.every(() => {
        const originLength = state.cachedViews.length;
        const inuseCacheNames = state.visitedViews.reduce(
          (inuseCacheNames, { matched }) => inuseCacheNames.concat(matched.map(({ name }) => name)),
          []
        );
        state.cachedViews = state.cachedViews.filter((E) => inuseCacheNames.includes(E));
        return originLength === state.cachedViews.length;
      });
    }
  }

3.AppMain.vue中,移除<router-view>的key

<template>
  <section class="app-main">
    <transition name="fade-transform" mode="out-in">
      <keep-alive :include="cachedViews">
        <router-view />
      </keep-alive>
    </transition>
  </section>
</template>

4.二级vue页面做一些改变:

  • AppMain.vue移除了key后,切换三级页面会丢失动画,要自己套一层<transition>
  • 为了解决切到其它二级路由所属页面后切回来三级路由错乱的问题,要在keep-alive激活后调用$forceUpdate
  • 如果页面不符时,调用$forceUpdate刷新会多一段额外的过渡动画,所以要自行控制过渡动画(示例中使用了isTransition变量,可自行调整逻辑)
<template>
  <transition :name="isTransition ? 'fade-transform' : ''" mode="out-in" @before-leave="1">
    <keep-alive :include="cachedViews">
      <router-view />
    </keep-alive>
  </transition>
</template>

<script>
export default {
  name: "记得填写name",
  data() {
    return { isTransition: true };
  },
  computed: {
    cachedViews() {
      return this.$store.state.tagsView.cachedViews;
    },
  },
  activated() {
    this.$forceUpdate();
    this.$nextTick(() => (this.isTransition = true));
  },
  deactivated() {
    this.isTransition = false;
  },
};
</script>

问题1: 二级vue页面是用的公共页面,只有一个name, 那岂不是每个二级路由都要新建一个路由页面 问题2: 怎么解决路由层级不确定的情况, 比如4层5层

  1. 按照你引用内容的方式,多个三级路由是共用一个二级路由的,之前的方法才会每个三级都配一个二级(因为使用了key),可以在vuetool中观察到
  2. 四、五层不在考虑范围内(毕竟标题写的只是三级路由),我的业务也只做到三级,没有多余的时间去测试更多层级是否合适

leekbillow avatar Jul 25 '22 01:07 leekbillow

接上楼,第一个问题看错了,想共用一个二级路由也不是不可以,所有三级共用一个二级name,菜单里二级路由也也都填同一个地址

leekbillow avatar Jul 25 '22 07:07 leekbillow

可以参考这个 https://github.com/PanJiaChen/vue-element-admin/issues/2391#issuecomment-633338531

trtrtr6 avatar Dec 15 '22 09:12 trtrtr6