項目開發中,經常會出現組件之間循環嵌套的問題,下面我們以 Element-Ui 中的 NavMenu
導航菜單組件二次封裝為例。暫不考慮 el-menu-item-group
的問題,將 NavMenu
拆分成如下的幾個小組件。
一、組件代碼
menu-item
menu-item
為原導航菜單組件的el-menu-item
,大致代碼如下:
<template>
<div class="meun-item">
<sub-menu v-if="menu.subMenu && menu.subMenu.length" :menu="menu" />
<router-link v-else :to="menu.path">
<el-menu-item :index="menu.menuName" :disabled="!!menu.disabled">
<template slot="title">
<i v-if="menu.icon" :class="['iconfont', menu.icon]"></i>
<span class="menu-text">{ { menu.label }}</span>
</template>
</el-menu-item>
</router-link>
</div>
</template>
<script>
import SubMenu from './sub-menu.vue'
export default {
props: {
menu: Object
},
components: {
SubMenu
}
}
</script>
sub-menu
sub-menu
為原導航菜單組件的el-submenu
,大致代碼如下:
<template>
<el-submenu :index="menu.menuName">
<template slot="title">
<i v-if="menu.icon" :class="['iconfont', menu.icon]"></i>
<span class="menu-text">{ { menu.label }}</span>
</template>
<menu-item
v-for="(item, index) in menu.subMenu || []"
:menu="item"
:key="index"
/> </el-submenu
></template>
<script>
import MenuItem from './menu-item.vue'
export default {
name: 'SubMenu',
props: {
menu: Object
},
components: {
MenuItem
}
}
</script>
nav-menu
nav-menu
為原導航菜單組件的el-menu
<template>
<el-menu class="nav-menu">
<template v-for="(menu, index) in menus">
<sub-menu
v-if="menu.subMenu && menu.subMenu.length"
:menu="menu"
:key="`sub-menu-${index}`"
/>
<menu-item v-else :menu="menu" :key="`menu-item-${index}`" />
</template> </el-menu
></template>
<script>
import MenuItem from './menu-item.vue'
import SubMenu from './sub-menu.vue'
export default {
props: {
menus: Array
},
components: {
MenuItem,
SubMenu
}
}
</script>
二、組件調用
<template>
<nav-menu :menus="menus"/>
</template>
<script>
import NavMenu from '@/components/nav-menu/nav-menu'
export default {
components: { NavMenu },
data () {
return {
menus: [
{ label: "菜單1", menuName: "1", path: "" },
{
label: "菜單2",
menuName: "2",
subMenu: [
{ label: "菜單2-1", menuName: "2-1", path: "" },
{
label: "菜單2-2",
menuName: "2-2",
subMenu: [{ label: "菜單2-2-1", menuName: "2-2-1", path: "" }],
},
],
},
{ label: "菜單3", menuName: "3" },
],
}
}
}
</script>
三、出現報錯情況
image
然后就會出現一個組件未注冊的報錯情況,然后仔細查代碼,看不是否有menu-item
拼錯了,發現沒有,都是對的,每次刷新頁面都會報這個錯誤,導致子集菜單不顯示。最后用排除大法,將menu-item
中的sub-menu
注釋掉后該報錯沒有了,目測問題應該是出現在這兒,但是sub-menu
也沒啥問題啊,百思不得其解,Google了半天才找到答案。
四、總結
歸根結底還是組件之間的循環引用造成的問題,正如上面的鏈接所說的,有兩個組件稱為 A 和 B(A、B就相當于這里的 menu-item
和 sub-menu
)。模塊系統發現它需要 A,但是首先 A 依賴 B,但是 B 又依賴 A,但是 A 又依賴 B,如此往復。這變成了一個循環,不知道如何不經過其中一個組件而完全解析出另一個組件。官方給的解決方法就是異步加載組件。將上面的 import
加載的方式全部變成異步加載,代碼如下:
const MenuItem = () => import('./menu-item.vue')
const SubMenu = () => import('./sub-menu.vue')
測試下了果然報錯就沒了。