介绍
Vue3 脚手架侧边栏
# 侧边栏的实现思路
- 通过 create-vue (opens new window) 脚手架创建的Vue3+vite项目时候 选择
Vue-router
路由组件后 想实现一个侧边栏效果 来记录和跳转我们写的路由页面 Vue-router
的component
父节点设置Layout
组件 子节点正常路由懒加载即可
- 侧边栏有哪些特性
- 可以展开收起 并且页面会对展开收起进行反应
- 通过事件判断是否展开 展开时设置路由的
margin-left
让其和侧边栏相同的宽度边距 收起的时候margin-left
可以设置为自定义收缩边距! - 设置
transition
过度效果 让展开收起更动画流畅
- 通过事件判断是否展开 展开时设置路由的
- 和路由配置一起生效
- 可能需要通过递归单独处理树形结构路由(按需)
- 侧边栏不能随滚动条滚动
- 给侧边栏设置
position: fixed
让其固定视图层上
- 给侧边栏设置
- 点击菜单后 跳转到指定路由
- 通过
router.push
获取点击的路由地址 跳转过去
- 通过
- 侧边栏自动获取当前路由地址(如果存在) 用于默认选中菜单
- 通过
route.path
获取当前路由地址(如果存在)
- 通过
- 可以展开收起 并且页面会对展开收起进行反应
# 实现代码
先设置好router路由配置
- 建议路由使用路由懒加载 (opens new window) 导入方式 这样可以优化页面加载 更高效
import { createRouter, createWebHashHistory } from 'vue-router'
import HomeView from '@/views/dashboard/index.vue'
// 导入通用配置
import { viewSettings } from '@/settings'
/* Layout */
// import Layout from '@/layout/index.vue'
// 使用动态导入/懒加载 导入我们的侧边栏组件
const Layout = () => import('@/layout/index.vue')
const routes = [
{
path: '/',
// 父节点使用侧边栏组件
component: Layout,
meta: <any>{
title: 'webgl学习目录'
},
children: [
{
path: '/',
name: 'HoMe',
// 首页无需按需加载 子节点不需要侧边栏组件
component: HomeView,
meta: {
title: '破碎的玻璃'
}
},
{
path: '/texture',
name: 'TexTure',
// 设置按需加载
component: () => import('@/views/texture/index.vue'),
meta: {
title: '纹理内容 '
}
},
]
}
]
const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL),
routes
})
// 设置路由title
router.beforeEach((to, from, next) => {
// 把路由title 和 默认title拼接起来
document.title = `${to.meta.title} - ${viewSettings.title}`
next()
})
export default router
export { routes }
然后再layout中 设置侧边栏和
<router-view>
- 通过n-layout (opens new window)布局 绑定我们的侧边栏 和
<router-view>
路由渲染- 处理
router
路由的时候 可能需要通过递归方式 处理一下路由让侧边栏能够使用 - 通过动态
class
类名的方式 来绑定展开收起时的样式效果
- 处理
<template>
<n-layout has-sider>
<!-- 左侧菜单 -->
<n-layout-sider
:native-scrollbar="false"
class="layOut"
@collapse="collapsed = true"
@expand="collapsed = false"
bordered
collapse-mode="width"
:collapsed="collapsed"
:collapsed-width="0"
default-expand-all
:width="240"
show-trigger
:inverted="inverted"
>
<div class="layoutColor">
换色
<n-switch v-model:value="inverted" />
</div>
<n-menu
default-expand-all
:default-value="defaultValue"
:inverted="inverted"
:collapsed-width="64"
:collapsed-icon-size="22"
:options="routerList"
key-field="path"
label-field="title"
@update:value="gotoRouter"
/>
</n-layout-sider>
<!-- 右侧路由内容1 -->
<router-view :class="['router-box', collapsed ? 'router-box-close' : 'router-box-open']" />
</n-layout>
</template>
<script lang="ts" setup>
// 导入naive
// 导入路由菜单
import { routes } from '@/router/index'
import { ref } from 'vue'
// 导入操作路由方法
import { useRouter, useRoute } from 'vue-router'
// 注册操作路由方法
const router = useRouter()
const route = useRoute()
// 开启对比色(黑暗/白色)
const inverted = ref(true)
// 设置展开/收起状态位
const collapsed = ref(false)
// 获取当前地址 用于设置默认选中菜单
const defaultValue = ref(route.path)
// 储存处理好的路由菜单
const routerList = ref([])
/**
* @description: 递归路由
* @param routerList 是一个空数组
* @param routes routes是路由菜单
* @returns
*/
const getRouterList = (routerList: any[], routes: any[]) => {
for (const item of routes) {
// 解构数据
const {
meta: { title = '暂无标题', icon = undefined },
path,
component
} = item
// 设置路由
const meun: any = {
title,
path,
icon,
component,
// 准备子节点容器
children: []
}
// 判断是否含有子节点
if (item.children) {
// 如果含有子节点 提取出子节点容器 并拿到数据的子节点再次进行添加
getRouterList(meun.children, item.children)
} else {
delete meun.children
}
routerList.push(meun)
}
return routerList
}
// 处理路由菜单
getRouterList(routerList.value, routes)
// 点击路由进行跳转
const gotoRouter = (e: string) => {
router.push(e)
}
</script>
<script lang="ts">
export default {
name: 'LayOut'
}
</script>
<style lang="scss" scoped>
.layOut {
// 设置fixed 使其不随滚动条滚动
position: fixed;
top: 0;
z-index: 99;
height: 100%;
width: 100%;
.layoutColor {
margin-block: 10px;
}
}
.router-box {
position: relative;
transition: margin-left ease-in-out 0.28s;
// 设置一个最小高度让路由撑满整个页面
min-height: 100vh;
}
// 开启的时候左边距
.router-box-open {
margin-left: 240px;
}
// 关不时候的左边距
.router-box-close {
margin-left: 0px;
}
</style>