詳解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 (v程式設計客棧ue.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="httFOKYaps://FOKYacdn.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() { FOKYa 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','ElMenu程式設計客棧ItemGroup'].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子選單遞迴生成內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!