当采用递归方式生成导航栏的子菜单时,菜单可以正常生成,但是当鼠标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)
版本:
<!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” 因此即使添加了这个属性,也依然是报错的,在此致歉!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
长按识别二维码并关注微信
更方便到期提醒、手机管理