[Vue3踩坑笔记] 记录Vue3+Antdv2中动态菜单收起/展开的问题(页面卡死)

4,809 阅读3分钟

最近在用Vue3+AntDesginVue2来搭建公司前端基础框架Simba-Admin-Vue

在做左侧菜单栏中路由与菜单项联动时遇到了一些问题。 我需要实现这样的效果:

不知道能不能称作antdv的bug,如果全部用submenu,那么各项菜单点击展开是没问题的,但如果同时使用menu-item和submenu,就会出现submenu不展开的问题。

场景如下: 顶层的单个菜单项menu-item高亮,然后打开某submenu,点击其中某子项,如用户管理>会员列表,此时submenu会突然收起来,并不会如我们所期望的自动对应展开。而且如果此时刷新页面,也不会自动展开路由对应菜单项所在的submenu。也可能是我学艺不精,漏了某个API,总之子级高亮父级自动展开效果以及手风琴效果我觉得从组件内部直接控制会更好吧。之前用iview还是element啥的,好像菜单有提供组件的实例方法,好歹还能控制。但看API发现,a-menu组件中并未提供openMenu这样的方法,只能通过设置openKeys来打开submenu。

好吧,说下最后怎么解决的。 因为我的菜单都是根据路由生成的,菜单的key就是路由中的meta.title,所以点击的时候,会执行路由跳转。所以可以监听路由,从路由中获取到信息,然后设置openKeys。大部分人用name作为key,害,我觉得用标题做key目前问题不大,后面我再改。

然后监听路由时,我记得vue2路由文档中好像有一段是watch:{ '$route'(){} }啥的,然后我就照猫画虎用了,也确实能用,但每次编译控制台就报很多错,页面卡死,CPU100%,关键build之后无法跳转页面。但因为本地编译报错触发者的是vue-i18n,我就一直在它的仓库找issues,后来又去antdv的仓库找,发现并没有什么相关的提问。然后我想报错都是在路由跳转时,是不是有可能在路由中出了问题,那造成卡死的无非就是死循环啊啥的,那就只有watch了,然后去了vue-router-next中看issues,发现有人提了问题,作者回复之后,还回了个微笑脸。好吧,是我们学艺不精,没认真看vue3的文档,改了watch之后就正常了。大家有空去看下vue3中watch api那段吧。

为避免后人浪费没必要的时间,作文以记之。欢迎有经验的同学讨论及指正。 感谢各位开源作者,你们是这个时代的神仙。

最后贴一下代码吧:

setup() {
    let route = useRoute();
    let {
      state: { system, account },
    } = useStore();
    //当前路由的标题,用于和子菜单对应
    let current_page_title = computed(() => route.meta.title);
    //当前路由所在模块的标题,用于和submenu对应
    let current_submenu_key = computed(() => route.matched[0].meta.title);
    //当前菜单是否收起
    let collapsed = computed(() => system.collapsed);
    //需要展开的菜单
    let openKeys = ref([current_submenu_key.value]);
    //路由对应的高亮的菜单KEY
    let selectedKeys = ref([current_page_title.value]);
    //根据路由生成的菜单数组
    let menus = computed(() => account.routes.filter((r) => !r.meta.hideInMenu));
    //不知道为啥需要,但手风琴效果要求写的submenu的keys
    let rootSubmenuKeys = menus.value.map((r) => r.meta.title);
    //想吐槽一下的手风琴效果,抄的官网
    function onOpenChange(oks) {
      const latestOpenKey = oks.find((key) => openKeys.value.indexOf(key) === -1);
      if (rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
        openKeys.value = oks;
      } else {
        openKeys.value = latestOpenKey ? [latestOpenKey] : [];
      }
    }
    //监听路由的地址变化,注意不要watch(route),否则CPU飙升
    watch(() => route.path,val=> {
        //当路由变化时,拿到当前路由所在模块的标题,用于展开对应的菜单
        openKeys.value = [current_submenu_key.value];
      }
    );

    return {
      //...
    };
  },