vue-element-admin的侧边栏逻辑分析1.源码位置
sidebar 引⽤⾃ layout 组件,layout 组件位于 src/layout/index.vue
sidebar 组件源码位于 src/layout/components/Sidebar/index.vue
2.el-menu ⽤法解析
侧边栏的核⼼是将根据权限过滤后的 router和el-menu 组件进⾏映射,所以熟悉 el-menu 是理解 sidebar 的起点<template>
<el-row class="tac">
<el-col :span="12">
<el-menu
default-active="1-1"
background-color="#545c64"
text-color="#fff"
active-text-color="#ffd04b"
mode="vertical"
unique-opened
:collapse="isCollapse"
:collapse-transition="false"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
@select="handleSelect"
>
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span>导航⼀</span>
</template>
<el-menu-item-group>
<template slot="title">分组⼀</template>
<el-menu-item index="1-1">选项1</el-menu-item>
<el-menu-item index="1-2">选项2</el-menu-item>
</el-menu-item-group>
<el-menu-item-group title="分组2">
<el-menu-item index="1-3">选项3</el-menu-item>
</el-menu-item-group>
<el-submenu index="1-4">
<template slot="title">选项4</template>
<el-menu-item index="1-4-1">选项1</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2">
<template slot="title">
<i class="el-icon-menu"></i>
<span slot="title">导航⼆</span>
</template>
<el-menu-item index="2-1">选项2-1</el-menu-item>
</el-submenu>
<el-menu-item index="3" disabled>
<i class="el-icon-document"></i>
<span slot="title">导航三</span>
</el-menu-item>
<el-menu-item index="4">
<i class="el-icon-setting"></i>
<span slot="title">导航四</span>
</el-menu-item>
</el-menu>
</el-col>
<el-col>
<el-button @click="isCollapse = !isCollapse">折叠</el-button>
</el-col>
</el-row>
</template>
<script>
export default {
data() {
return {
isCollapse: false
}
},
methods: {
handleSelect(key, keyPath) {
console.log('handleSelect', key, keyPath)
},
handleOpen(key, keyPath) {
console.log('handleOpen', key, keyPath)
},
handleClose(key, keyPath) {
console.log('handleClose', key, keyPath)
}
}
}
</script>
2.1.el-menu
el-menu 表⽰菜单容器组件:
default-active:激活的菜单,注意如果存在⼦菜单,需要填⼊⼦菜单 ID
unique-opened:是否保持⼀个菜单打开
mode:枚举值,分为 vertical 和 horizontal 两种
collapse:是否⽔平折叠收起菜单(仅在 mode 为 vertical 时可⽤)
collapse-transition:是否显⽰折叠动画
@select:点击菜单事件,keyPath 代表菜单的访问路径,如:1-4-1 菜单的点击⽇志为:
handleSelect 1-4-1 (3) ["1", "1-4", "1-4-1"]
获取 keyPath 我们可以获取 1-4-1 菜单的所有⽗级菜单的 ID
@open:⽗菜单打开时触发事件
@close:⽗菜单关闭时触发事件
2.2.el-submenu
⼦菜单容器,el-submenu 与 el-menu 不同,el-menu 表⽰整个菜单,⽽ el-submenu 表⽰⼀个具体菜单,只是该菜单还包含了⼦菜单
el-submenu 可以通过定制 slot 的 title 来⾃定义菜单样式:
<el-submenu index="1">
<template slot="title">
<i class="el-icon-location"></i>
<span>导航⼀</span>
</template>
</el-submenu>
el-submenu 容器内 default 的 slot ⽤来存放⼦菜单,可以包含三种⼦菜单组件:
el-menu-item-group:菜单分组,为⼀组菜单添加⼀个标题,el-menu-item-group容器内容需要存放el-menu-item组件,⽀持通过 title 的 slot 来定制标题样式
el-submenu:el-submenu⽀持循环嵌套el-submenu,这使得超过两级⼦组件得以实现
el-menu-item:⼦菜单组件
3.sidebar 源码分析
sidebar 源码如下:
<template>
<div :class="{'has-logo':showLogo}">
<logo v-if="showLogo" :collapse="isCollapse"/>
<el-scrollbar wrap-class="scrollbar-wrapper">
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="uBg"
:text-color="uText"
:unique-opened="false"
:active-text-color="uActiveText"
:collapse-transition="false"
mode="vertical"
>
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path"/>
</el-menu>
</el-scrollbar>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import Logo from './Logo'
import SidebarItem from './SidebarItem'
import variables from '@/styles/variables.scss'
export default {
components: { SidebarItem, Logo },
computed: {
...mapGetters([
'permission_routes',
'sidebar'
]),
activeMenu() {
const route = this.$route
const { meta, path } = route
if (meta.activeMenu) {
return meta.activeMenu
}
return path
},
showLogo() {
return this.$store.state.settings.sidebarLogo
},
variables() {
return variables
},
isCollapse() {
return !this.sidebar.opened
}
}
}
</script>
activeMenu:通过 meta.activeMenu 属性,指定路由对应的⾼亮菜单,meta.activeMenu 需要提供⼀个合法的路由,否则将不能⽣效isCollapse:NavBar 中点击按钮,会修改 Cookie 中的 sidebarStatus,从 vuex 取值时会将 sidebarStatus 转为 Boolean,并判断默认是否需要收缩左侧菜单栏
showLogo:判断 settings.js 中的配置项是否需要展⽰ Logo
variables:从@/styles/variables.scss中获取 scss 对象,从⽽获取样式
sidebar 中通过 sidebar-item 实现⼦菜单,下⾯我们来分析 sidebar-item 组件
4.sidebar-item 源码分析
side-item 组件源码如下:
<template>
<div v-if="!item.hidden" class="menu-wrapper">
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||ShowingChildren)&&!item.alwaysShow">
<app-link v-if="a" :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item :icon="a.icon||(a&&a.icon)" :title="a.title"/>
</el-menu-item>
</app-link>
</template>
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
<template slot="title">
<item v-if="a" :icon="a && a.icon" :title="a.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>
</div>
</template>
<script>
import path from 'path'
import { isExternal } from '@/utils/validate'
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 github/PanJiaChen/vue-admin-template/issues/237
/
/ TODO: refactor with render function
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)
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) {
return true
}
return false
},
resolvePath(routePath) {
if (isExternal(routePath)) {
return routePath
}
if (isExternal(this.basePath)) {
return this.basePath
}
solve(this.basePath, routePath)
}
}
}
</script>
4.1.side-item props 分析
side-item 的 props 如下:
item:路由对象
basePath:路由路径
4.2.sidebar-item 展⽰逻辑分析
vue element adminsidebar-item 最重要是展⽰逻辑,主要分为以下⼏步:
通过 item.hidden 控制菜单是否展⽰
通过hasOneShowingChild(item.children,item) && (!onlyOneChild.children||ShowingChildren)&&!item.alwaysShow逻辑判断 template 菜单是否展⽰,template 代表单⼀菜单;
hasOneShowingChild:判断是否只有⼀个需要展⽰的⼦路由
!onlyOneChild.children||ShowingChildren:判断需要展⽰的⼦菜单,是否包含 children 属性,如果包含,则说明⼦菜单可能存在孙⼦菜单,此时则需要再判断 noShowingChildren 属性
!item.alwaysShow:判断路由中是否存在 alwaysShow 属性,如果存在,则返回 false,不展⽰ template 菜单,也就说只要配置了alwaysShow 属性就会直接进⼊ el-submenu 组件
hasOneShowingChild⽅法源码详解
⼊参:
children:router 对象的 children 属性
item:router 对象
hasOneShowingChild(children = [], parent) {
const showingChildren = children.filter(item => {
// 如果 children 中的路由包含 hidden 属性,则返回 false
if (item.hidden) {
return false
} else {
// 将⼦路由赋值给 onlyOneChild,⽤于只包含⼀个路由时展⽰
return true
}
})
// 如果过滤后,只包含展⽰⼀个路由,则返回 true
if (showingChildren.length === 1) {
return true
}
// 如果没有⼦路由需要展⽰,则将 onlyOneChild 的 path 设置空路由,并添加 noShowingChildren 属性,表⽰虽然有⼦路由,但是不需要展⽰⼦路由
if (showingChildren.length === 0) {
return true
}
// 返回 false,表⽰不需要展⽰⼦路由,或者超过⼀个需要展⽰的⼦路由
return false
}
如果展⽰ template 组件,⾸先会展⽰ app-link 组件,然后是 el-menu-item,最⾥⾯嵌套的是 item 组件:
item 组件需要路由 meta 中包含 title 和 icon 属性,否则将渲染内容为空的 vnode 对象
<app-link v-if="a" :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}">
<item :icon="a.icon||(a&&a.icon)" :title="a.title"/>
</el-menu-item>
</app-link>
如果 template 菜单不展⽰,则展⽰ el-submenu 菜单,el-submenu 逻辑中采⽤了嵌套组件的做法,将 sidebar-item 嵌套在 el-submenu 中:
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
<template slot="title">
<item v-if="a" :icon="a && a.icon" :title="a.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>
el-submenu 中的 sidebar-item 有两点区别:
第⼀是传⼊ is-nest 参数
第⼆是传⼊ base-path 参数
5.app-link 源码分析
app-link 是⼀个动态组件,通过解析 to 参数,如果包含 http 前缀则变成⼀个 a 标签,否则变成⼀个 router-link 组件
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论