Vue项目中用递归生成子菜单报错问题怎么办

Admin 2022-10-21 群英技术资讯 499 次浏览

今天这篇给大家分享的知识是“Vue项目中用递归生成子菜单报错问题怎么办”,小编觉得挺不错的,对大家学习或是工作可能会有所帮助,对此分享发大家做个参考,希望这篇“Vue项目中用递归生成子菜单报错问题怎么办”文章能帮助大家解决问题。

当采用递归方式生成导航栏的子菜单时,菜单可以正常生成,但是当鼠标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” 因此即使添加了这个属性,也依然是报错的,在此致歉!


感谢各位的阅读,以上就是“Vue项目中用递归生成子菜单报错问题怎么办”的内容了,经过本文的学习后,相信大家对Vue项目中用递归生成子菜单报错问题怎么办都有更深刻的体会了吧。这里是群英网络,小编将为大家推送更多相关知识点的文章,欢迎关注! 群英智防CDN,智能加速解决方案
标签: 生成子菜单

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。

猜你喜欢

成为群英会员,开启智能安全云计算之旅

立即注册
专业资深工程师驻守
7X24小时快速响应
一站式无忧技术支持
免费备案服务
免费拨打  400-678-4567
免费拨打  400-678-4567 免费拨打 400-678-4567 或 0668-2555555
在线客服
微信公众号
返回顶部
返回顶部 返回顶部
在线客服
在线客服