當採用遞歸方式生成導航欄的子菜單時,菜單可以正常生成,但是當鼠標hover時,會出現循環調用某個(mouseenter)事件,導致最後報錯
報錯信息如下:
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">
</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事件,造成了類似死循環的狀態,因時間關係,沒做深究,後面有時間的時候再查下根本原因(如果記得的話…)
處理方式
1.給el-submenu添加一個屬性:
popper-append-to-body:true
將二級子菜單插入至body,這樣做之後需要根據需要添加一些樣式
2.給組件最外圍添加一個div
這是第一個的嘗試,能夠使子菜單大致正常使用,但是包裹之後,又需要給div添加
display:inline-block
,否則樣式走樣嚴重,並且這樣使用,會出現一級菜單排列順序異常的問題,當然可以用一些方法處理,比如通過某個值強制確定位置,但是個人認爲沒必要搞那麼麻煩,所以推薦第一種處理方式。