Vue.js 侧边栏菜单组件:SidebarItem 实现与逻辑详解
<template>
<div v-if="!item.hidden" class="menu-wrapper">
<template v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren) && !item.alwaysShow">
<app-link v-if="item.redirect != 'noRedirect' && onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
<el-menu-item
:index="resolvePath(onlyOneChild.path)"
:class="{ 'submenu-title-noDropdown': !isNest }"
>
{{ onlyOneChild.meta.title }}
</el-menu-item>
</app-link>
</template>
<pre><code><el-submenu
v-else
ref="subMenu"
:index="resolvePath(item.path)"
popper-append-to-body
>
<template slot="title">
<item
v-if="item.meta"
:icon="item.meta && item.meta.icon"
:title="item.meta.title"
/>
</template>
<sidebar-item
v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="resolvePath(child.path)"
class="nest-menu"
/>
</el-submenu>
</code></pre>
</div>
</template>
<script>
import path from 'path'
import { Validator } from '@bigbighu/cms-utils'
import Item from './Item'
import AppLink from './Link'
import FixiOSBug from './FixiOSBug'
export default {
name: 'SidebarItem',
components: { Item, AppLink },
mixins: [FixiOSBug],
props: {
// route object
item: {
type: Object,
required: true
},
isNest: {
type: Boolean,
default: false
},
basePath: {
type: String,
default: ''
}
},
data() {
// To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
// TODO: refactor with render function
this.onlyOneChild = null
return {}
},
methods: {
hasOneShowingChild(children = [], parent) {
const showingChildren = children.filter(item => {
if (item.hidden) {
return false
} else {
// Temp set(will be used if only has one showing child)
this.onlyOneChild = item
return true
}
})
// When there is only one child router, the child router is displayed by default
// if (showingChildren.length === 1) {
// return true
// }
// Show parent if there are no child router to display
if (showingChildren.length === 0) {
this.onlyOneChild = { ...parent, path: '', noShowingChildren: true }
return true
}
console.log(children, 'children')
return false
},
resolvePath(routePath) {
console.log(this.onlyOneChild, 'onlyOneChild')
console.log(path.resolve(this.basePath, routePath), 'routePath')
if (Validator.isExternal(routePath)) {
return routePath
}
if (Validator.isExternal(this.basePath)) {
return this.basePath
}
return path.resolve(this.basePath, routePath)
}
}
}
</script>
<style lang="less" scope>
#app .hideSidebar .el-submenu > .el-submenu__title .iconfont {
margin-left: 18px;
}
</style>
<h2>代码详解</h2>
<p>该代码片段是一个 Vue.js 侧边栏菜单组件 <code>SidebarItem</code> 的实现。该组件接收一个路由对象作为 <code>item</code> 属性,并根据该对象的信息递归地渲染菜单项。</p>
<p>组件的主要逻辑如下:</p>
<ol>
<li>
<p><strong>判断是否显示菜单项</strong>: 首先,组件会根据 <code>item.hidden</code> 属性判断是否需要隐藏该菜单项。如果 <code>item.hidden</code> 为 true,则不会渲染该菜单项。</p>
</li>
<li>
<p><strong>判断是否只有一个子菜单项需要显示</strong>: 接着,组件会判断该菜单项是否有子菜单项,以及是否有子菜单项需要显示。如果只有一个子菜单项需要显示,则会使用 <code>el-menu-item</code> 组件渲染该子菜单项。</p>
</li>
<li>
<p><strong>判断是否需要重定向</strong>: 如果该菜单项有 <code>redirect</code> 属性,并且 <code>redirect</code> 属性的值不为 <code>noRedirect</code>,则会使用 <code>app-link</code> 组件渲染一个链接,该链接会指向子菜单项的路径。</p>
</li>
<li>
<p><strong>递归渲染子菜单项</strong>: 如果该菜单项有多个子菜单项,则会使用 <code>el-submenu</code> 组件渲染一个子菜单。子菜单中的子菜单项会使用 <code>sidebar-item</code> 组件递归地渲染。</p>
</li>
<li>
<p><strong>处理 <code>basePath</code></strong>: 组件会使用 <code>resolvePath</code> 方法来处理 <code>basePath</code> 属性。<code>basePath</code> 属性用于指定当前菜单项的根路径,该路径会被用于计算子菜单项的路径。</p>
</li>
</ol>
<h2>代码中的关键点</h2>
<ul>
<li><code>hasOneShowingChild</code> 方法用于判断是否只有一个子菜单项需要显示。</li>
<li><code>resolvePath</code> 方法用于处理 <code>basePath</code> 属性。</li>
<li><code>el-submenu</code> 和 <code>el-menu-item</code> 组件是 Element UI 框架中的菜单组件。</li>
<li><code>app-link</code> 组件是一个自定义组件,用于渲染链接。</li>
<li><code>Validator.isExternal</code> 方法用于判断一个路径是否是外部路径。</li>
</ul>
<h2>总结</h2>
<p>这段代码是一个典型的 Vue.js 侧边栏菜单组件实现,它使用了递归、条件判断和路径处理等技术,实现了根据路由信息动态渲染菜单的功能。开发者可以根据自己的需求修改该组件,以实现更加复杂的菜单功能。</p>
原文地址: https://www.cveoy.top/t/topic/nDhA 著作权归作者所有。请勿转载和采集!