詳解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 (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” 因此即使添加瞭這個屬性,也依然是報錯的,在此致歉!
到此這篇關於詳解Element-ui NavMenu子菜單使用遞歸生成時使用報錯的文章就介紹到這瞭,更多相關Element-ui NavMenu子菜單遞歸生成內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- None Found