项目开发中,经常会出现组件之间循环嵌套的问题,下面我们以 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>
三、出现报错情况
然后就会出现一个组件未注册的报错情况,然后仔细查代码,看不是否有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')
测试下了果然报错就没了。