100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > sidebar(侧边栏原理vue admin)

sidebar(侧边栏原理vue admin)

时间:2021-07-04 12:01:15

相关推荐

sidebar(侧边栏原理vue admin)

源码位置

sidebar 引用自 layout 组件,layout 组件位于 src/layout/index.vuesidebar 组件源码位于 src/layout/components/Sidebar/index.vue

el-menu 用法解析

侧边栏的核心是将根据权限过滤后的 router 和 el-menu 组件进行映射,所以熟悉 el-menu 是理解 sidebar 的起点

<template><el-row class="tac"><el-col :span="12"><el-menu // el-menu本身是一个菜单容器default-active="1-1" // 默认菜单几高亮显示background-color="#545c64" // 背景色text-color="#fff" // 文本颜色active-text-color="#ffd04b" // 激活后文本颜色mode="vertical" // 模式 vertical垂直模式 horizontal 水平模式unique-opened // 是不是之能同时打开一个菜单 :unique-opened:"false" 允许俩者同时被打开:collapse="isCollapse"// 默认的折叠状态:collapse-transition="false" // 是否需要展示一个collapse动画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>

el-menu

el-menu 表示菜单容器组件:

default-active:激活的菜单,注意如果存在子菜单,需要填入子菜单 IDunique-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:父菜单关闭时触发事件

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:子菜单组件

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="variables.menuBg":text-color="variables.menuText":unique-opened="false":active-text-color="variables.menuActiveText":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.$routeconst {meta, path } = routeif (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 中的配置项是否需要展示 Logovariables:从 @/styles/variables.scss 中获取 scss 对象,从而获取样式

sidebar 源码中应用的一些技巧

sidebar 中通过 sidebar-item 实现子菜单,下面我们来分析 sidebar-item 组件

sidebar-item 源码分析

side-item 组件源码如下:

<template><div v-if="!item.hidden" class="menu-wrapper"> //hidden为true时不进行展示//是否只有一个子组件<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow"><app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"><el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}"><item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.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="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /></template><sidebar-itemv-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 objectitem: {type: Object,required: true},isNest: {type: Boolean,default: false},basePath: {type: String,default: ''}},data() {// To fix /PanJiaChen/vue-admin-template/issues/237// TODO: refactor with render functionthis.onlyOneChild = nullreturn {}},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 = itemreturn true}})// When there is only one child router, the child router is displayed by defaultif (showingChildren.length === 1) {return true}// Show parent if there are no child router to displayif (showingChildren.length === 0) {this.onlyOneChild = {... parent, path: '', noShowingChildren: true }return true}return false},resolvePath(routePath) {if (isExternal(routePath)) {return routePath}if (isExternal(this.basePath)) {return this.basePath}return path.resolve(this.basePath, routePath)}}}</script>

side-item props 分析

side-item 的 props 如下:

item:路由对象basePath:路由路径

sidebar-item 展示逻辑分析

sidebar-item 最重要是展示逻辑,主要分为以下几步:

通过 item.hidden 控制菜单是否展示

通过 hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow 逻辑判断 template 菜单是否展示,template 代表单一菜单;

+ hasOneShowingChild:判断是否只有一个需要展示的子路由+ !onlyOneChild.children||onlyOneChild.noShowingChildren:判断需要展示的子菜单,是否包含 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 属性,则返回 falseif (item.hidden) {return false} else {// 将子路由赋值给 onlyOneChild,用于只包含一个路由时展示 this.onlyOneChild = itemreturn true}})// 如果过滤后,只包含展示一个路由,则返回 trueif (showingChildren.length === 1) {return true}// 如果没有子路由需要展示,则将 onlyOneChild 的 path 设置空路由,并添加 noShowingChildren 属性,表示虽然有子路由,但是不需要展示子路由if (showingChildren.length === 0) {this.onlyOneChild = {...parent, path: '', noShowingChildren: true }return true}// 返回 false,表示不需要展示子路由,或者超过一个需要展示的子路由return false}

如果展示 template 组件,首先会展示 app-link 组件,然后是 el-menu-item,最里面嵌套的是 item 组件:

item 组件需要路由 meta 中包含 title 和 icon 属性,否则将渲染内容为空的 vnode 对象

<app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"><el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}"><item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.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="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /></template><sidebar-itemv-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 参数

app-link 源码分析

app-link 是一个动态组件,通过解析 to 参数,如果包含 http 前缀则变成一个 a 标签,否则变成一个 router-link 组件

<template><!-- eslint-disable vue/require-component-is --><component v-bind="linkProps(to)"><slot /></component></template><script>import {isExternal } from '@/utils/validate'export default {props: {to: {type: String,required: true}},methods: {linkProps(url) {if (isExternal(url)) {return {is: 'a',href: url,target: '_blank',rel: 'noopener'}}return {is: 'router-link',to: url}}}}</script>

isExternal 函数通过一个正则表达式匹配 http 链接:

export function isExternal(path) {return /^(https?:|mailto:|tel:)/.test(path)}

item 组件源码分析

item 组件通过定义 render 函数完成组件渲染

<script>export default {name: 'MenuItem',functional: true,props: {icon: {type: String,default: ''},title: {type: String,default: ''}},render(h, context) {const {icon, title } = context.propsconst vnodes = []if (icon) {vnodes.push(<svg-icon icon-class={icon}/>)}if (title) {vnodes.push(<span slot='title'>{(title)}</span>)}return vnodes}}</script>

总结

+ sidebar:sidebar 主要包含 el-menu 容器组件,el-menu 中遍历 vuex 中的 routes,生成 sidebar-item 组件。sidebar 主要配置项如下:activeMenu:根据当前路由的 meta.activeMenu 属性控制侧边栏中高亮菜单isCollapse:根据 Cookie 的 sidebarStatus 控制侧边栏是否折叠variables:通过 @/styles/variables.scss 填充 el-menu 的基本样式+ sidebar-item:sidebar-item 分为两部分:第一部分是当只需要展示一个 children 或者没有 children 时进行展示,展示的组件包括:+ app-link:动态组件,path 为链接时,显示为 a 标签,path 为路由时,显示为 router-link 组件+ el-menu-item:菜单项,当 sidebar-item 为非 nest 组件时,el-menu-item 会增加 submenu-title-noDropdown 的 class+ item:el-menu-item 里的内容,主要是 icon 和 title,当 title 为空时,整个菜单项将不会展示第二部分是当 children 超过两项时进行展示,展示的组件包括:+ el-submenu:子菜单组件容器,用于嵌套子菜单组件+ sidebar-item:el-submenu 迭代嵌套了 sidebar-item 组件,在 sidebar-item 组件中有两点变化:+ 设置 is-nest 属性为 true+ 根据 child.path 生成了 base-path 属性传入 sidebar-item 组件

Vue admin

登陆流程

界面精简及路由处理(实现了权限控制)

路由和权限校验原理

侧边栏

重定向

面包屑导航

requst库 axios拦截

登录组件实现细节

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。