详解Element-ui NavMenu子菜单使用递归生成时使用报错

 更新时间:2021年04月22日 14:42:20   作者:Poedu05守護者  
这篇文章主要介绍了详解Element-ui NavMenu子菜单使用递归生成时使用报错,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

当采用递归方式生成导航栏的子菜单时,菜单可以正常生成,但是当鼠标hover时,会出现循环调用某个(mouseenter)事件,导致最后报错

处理方式

注:2.13.2 版本,只需对子菜单设置属性 :popper-append-to-body="false" 就不会出现这个问题了

报错信息如下:

Uncaught RangeError: Maximum call stack size exceeded.
    at VueComponent.handleMouseenter (index.js:1)
    at invokeWithErrorHandling (vue.js:1863)
    at HTMLLIElement.invoker (vue.js:2188)
    at HTMLLIElement.original._wrapper (vue.js:7547)
    at VueComponent.handleMouseenter (index.js:1)
    at invokeWithErrorHandling (vue.js:1863)
    at HTMLLIElement.invoker (vue.js:2188)
    at HTMLLIElement.original._wrapper (vue.js:7547)
    at VueComponent.handleMouseenter (index.js:1)
    at invokeWithErrorHandling (vue.js:1863)

测试代码

版本:

  • vue: v2.6.11
  • element-ui: 2.13.0
<!DOCTYPE html>
<html>
 <head>
  <meta charset="utf-8">
  <title></title>
  <!-- 引入样式 -->
  <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css" rel="external nofollow" >
 </head>
 <body>

  <div id="root">
   <el-menu mode="horizontal">
    <template v-for="(menu,index) in menus">
     <sub-menu v-if="menu.children && menu.children.length" :key="index" :item="menu"></sub-menu>
     <el-menu-item v-else :index="menu.path" :key="index">{{ menu.title }}</el-menu-item>
    </template>
   </el-menu>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <!-- 引入组件库 -->
  <script src="https://unpkg.com/element-ui/lib/index.js"></script>
  <script type="text/javascript">
   Vue.component('sub-menu', {
    props: ['item'],
    template: `
     <el-submenu :index="item.path">
      <template slot="title">
       {{item.title}}
      </template>
      <template v-for="(child,index) in item.children">
       <sub-menu v-if="child.children" :item="child" :key="index"></sub-menu>
       <el-menu-item v-else :key="index" :index="child.path">
        {{child.title}}
       </el-menu-item>
      </template>
     </el-submenu>
    `
   })

   let vm = new Vue({
    el: '#root',
    data() {
     return {
      menus: [{
       title: '我的工作台',
       path: '2',
       children: [{
         title: '选项1',
         path: '2-1'
        },
        {
         title: '选项2',
         path: '2-2',
        },
       ],
      },{
       title:'后台管理',
       path:'3'
      }]
     }
    },
    components: {}
   })
  </script>
 </body>
</html>

错误分析

观察递归生成的导航栏代码及报错代码:

 handleMouseenter: function(e) {
                    var t = this
                      , i = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : this.showTimeout;
                    if ("ActiveXObject"in window || "focus" !== e.type || e.relatedTarget) {
                        var n = this.rootMenu
                          , r = this.disabled;
                        "click" === n.menuTrigger && "horizontal" === n.mode || !n.collapse && "vertical" === n.mode || r || (this.dispatch("ElSubmenu", "mouse-enter-child"),
                        clearTimeout(this.timeout),
                        this.timeout = setTimeout(function() {
                            t.rootMenu.openMenu(t.index, t.indexPath)
                        }, i),
                        this.appendToBody && this.$parent.$el.dispatchEvent(new MouseEvent("mouseenter")));//报错代码
                    }
                },

猜测是因为事件冒泡或下沉导致元素重复派发和接受mouseenter事件,造成了类似死循环的状态,因时间关系,没做深究,后面有时间的时候再查下根本原因(如果记得的话…)

当鼠标移入到菜单中时,触发handleMouseenter方法,但是因为appendToBody为true,所以又派发了鼠标移入事件,然后又回到了这个方法,由此造成了死循环。appendToBody是一个计算属性,那么为什么appendToBody会是true呢?看代码:

{
 name: 'ElSubmenu',
    componentName: 'ElSubmenu',
 props:{
  popperAppendToBody: {
         type: Boolean,
         default: undefined   
        }
    },
    computed:{
  appendToBody() {
        return this.popperAppendToBody === undefined     
          ? this.isFirstLevel              //未显示指明popperAppendToBody 时,计算这个值
          : this.popperAppendToBody;
      },
      isFirstLevel() {
        let isFirstLevel = true;
        let parent = this.$parent;
        while (parent && parent !== this.rootMenu) {
        
        //计算当前是否时第一级菜单。
        //看上去是没问题的,因为代码里已经指明了当前的组件名是 componentName: 'ElSubmenu', 但是在调试中发现,componentName的值是Undefined, 因此不管是在哪一级,最后的结果都是 isFirstLevel = true 
          if (['ElSubmenu', 'ElMenuItemGroup'].indexOf(parent.$options.componentName) > -1) {
            isFirstLevel = false;
            break;
          } else {
            parent = parent.$parent;
          }
        }
        return isFirstLevel;
      }
 }
}

至于为什么vue在组件注册时没有收集这个参数,还需要从源码那边看,午休时间过了,要继续撸代码了…得空了再分析一下…

处理方式

给el-submenu添加一个属性 :popper-append-to-body=“true false” 显式的指明appendToBody为false

特别致歉:

此前的处理方式写错了,写的是:popper-append-to-body=“true” 因此即使添加了这个属性,也依然是报错的,在此致歉!

到此这篇关于详解Element-ui NavMenu子菜单使用递归生成时使用报错的文章就介绍到这了,更多相关Element-ui NavMenu子菜单递归生成内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Vue优化:常见会导致内存泄漏问题及优化详解

    Vue优化:常见会导致内存泄漏问题及优化详解

    这篇文章主要介绍了Vue优化:常见会导致内存泄漏问题及优化详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08
  • 实现elementUI表单的全局验证的方法步骤

    实现elementUI表单的全局验证的方法步骤

    这篇文章主要介绍了实现elementUI表单的全局验证的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • Vue实现简单分页器

    Vue实现简单分页器

    这篇文章主要为大家详细介绍了Vue实现简单分页器,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • vue router 通过路由来实现切换头部标题功能

    vue router 通过路由来实现切换头部标题功能

    在做单页面应用程序时,一般页面布局头尾两块都是固定在布局页面,中间为是路由入口。这篇文章主要介绍了vue-router 通过路由来实现切换头部标题 ,需要的朋友可以参考下
    2019-04-04
  • vue实现tab标签(标签超出自动滚动)

    vue实现tab标签(标签超出自动滚动)

    当创建的tab标签超出页面可视区域时自动滚动一个tab标签距离,并可手动点击滚动tab标签,需要的朋友们下面随着小编来一起学习学习吧
    2021-05-05
  • Vue中v-for的数据分组实例

    Vue中v-for的数据分组实例

    下面小编就为大家分享一篇Vue中v-for的数据分组实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-03-03
  • vuex中Getter的用法详解

    vuex中Getter的用法详解

    这篇文章主要给大家介绍了关于Vuex中Getter的基本使用教程,getter相当于Vuex中的计算属性 对 state 做处理再返回,本文通过示例代码将Getter介绍的非常详细,需要的朋友可以参考下
    2021-07-07
  • Webpack+Vue如何导入Jquery和Jquery的第三方插件

    Webpack+Vue如何导入Jquery和Jquery的第三方插件

    本文主要介绍了Webpack+Vue导入Jquery和Jquery的第三方插件的方法,具有很好的参考价值,下面跟着小编一起来看下吧
    2017-02-02
  • Vue2.x Todo之自定义指令实现自动聚焦的方法

    Vue2.x Todo之自定义指令实现自动聚焦的方法

    我们希望用户双击 todo 进入编辑状态后输入框自动获取焦点,而不是需要先手动点一下。这篇文章主要介绍了Vue 2.x Todo之自定义指令实现自动聚焦,非常具有实用价值,需要的朋友可以参考下
    2019-01-01
  • vue工程全局设置ajax的等待动效的方法

    vue工程全局设置ajax的等待动效的方法

    这篇文章主要介绍了vue工程全局设置ajax的等待动效的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-02-02

最新评论