Element-ui NavMenu子菜單使用遞歸生成時使用報錯

當採用遞歸方式生成導航欄的子菜單時,菜單可以正常生成,但是當鼠標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,否則樣式走樣嚴重,並且這樣使用,會出現一級菜單排列順序異常的問題,當然可以用一些方法處理,比如通過某個值強制確定位置,但是個人認爲沒必要搞那麼麻煩,所以推薦第一種處理方式

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章