定义静态路由
// 定义静态路由
const routes = [
{
path: '/',
isHidden: true, // 不在导航列表中显示
redirect: '/layout'
},
{
path: '/login',
isHidden: true, // 不在导航列表中显示
// 使用 路由✨懒加载
component: () => import('@/views/login/comLogin.vue')
},
{
path: '/register',
isHidden: true, // 不在导航列表中显示
component: () => import('@/views/register/comRegister.vue')
},
{
path: '/layout',
component: layout,
redirect: '/layout/home', // 当路由未匹配时重定向,可作初始化显示页面设置
children: [
{
path: 'home',
meta: {
title: '首页',
icon: ''
},
component: () => import('@/components/home.vue')
}
]
},
{
path: '/layout',
leaf: true, // 有二级路由
component: layout,
name: '/layout/zi',
meta: {
title: '个人中心',
icon: ''
},
children: [
{
path: 'user-info',
meta: {
title: '基本资料',
icon: ''
},
component: () => import('@/components/person-center/userInfo.vue')
},
{
path: 'user-avatar',
meta: {
title: '更换头像',
icon: ''
},
component: () => import('@/components/person-center/changePic.vue')
},
{
path: 'user-pwd',
meta: {
title: '重置密码',
icon: ''
},
component: () => import('@/components/person-center/resetPassword.vue')
}
]
}
]
服务器获取的路由数据格式
{
path: '/layout',
leaf: true, // 有二级路由
component: 'layout',
name: 'articleManagement',
meta: {
title: '文章管理',
icon: ''
},
children: [
{
path: 'art-cate',
meta: {
title: '文章分类',
icon: ''
},
component: '/article-manage/articleCase'
},
{
path: 'art-list',
meta: {
title: '文章列表',
icon: ''
},
component: '/article-manage/articleList'
}
]
}
定义处理组件路径的函数
function filterAsyncRouter() {
// 用深拷贝处理一下赋值操作, JSON.parse(JSON.stringify 这一步不写也可以
const router = JSON.parse(JSON.stringify($store.state.router)) // vuex 的路由信息
router.forEach(item => {
// 处理布局数组件的路径
item.component = () => import('@/views/layout/comLayout.vue')
if (item.children) {
item.children.forEach(it => {
const path = it.component
// webpack4的问题,不能直接使用字符变量作为引用,而且 it.component 不能直接写在路径里面
// 我也不知道原因直接写会找不到模块,必须用一个变量先接收,可能有其他方法我还不清楚
it.component = (resolve) => require([`@/components${path}.vue`], resolve)
})
}
})
return router
}
定义动态添加路由的函数
// 定义404页面 对于用户自己手动的用输入链接访问路由表中不存在的路由组件,那么就直接提示无权访问.
const touter_404 = {
path: '*',
name: '404',
isHidden: true, // 不在导航列表中显示
component: () => import('@/views/error-page/404.vue')
}
/**
* 添加路由
* @param {Object} to 要跳转的path
* @param {function} next 下一步
*
*/
function routerGo(to, next) {
const getRouter = filterAsyncRouter() // 过滤路由转换路由的 component
// 把404页面放到路由表的最后,避免动态路由刷新404
getRouter.push(touter_404)
// router.options.routes 静态路由
const routerArr = router.options.routes // 获取全部路由(静态加动态)
// 循环添加路由
getRouter.forEach(item => {
router.addRoute(item)
routerArr.push(item)
})
hasRoles = false // 添加完路由赋值为 false 下次不会再添加路由,页面刷新时又会变成true
// 把路由数据添加到Vuex 生成菜单
$store.commit('updateRouterArr', routerArr)
next({ ...to, replace: true })
}
在导航守卫内动态添加路由
// 记录路由 页面刷新之后这里会冲新定义,解决刷新页面动态路由丢失的问题
let hasRoles = true
// 发生页面跳转时触发
router.beforeEach(async (to, from, next) => {
const token = $store.state.token
// 权限访问控制
if (token) {
// 如果有token, 证明已登录
if (!$store.state.userInfo.username) {
// 有token但是没有用户信息, 才去请求用户信息保存到vuex里
// 调用actions里方法请求数据
$store.dispatch('initUserInfo') // 下次切换页面vuex里有用户信息数据就不会重复请求用户信息
await $store.dispatch('initRouter') // 获取路由信息
}
if (hasRoles) { // 添加动态路由
routerGo(to, next)
} else { // 没有动态路由的情况
next()
}
} else {
// 如果无token
// 【因为登录、注册页面也没有token,根据全局前置守卫的定义不做白名单处理,会一直处于跳转——触发——跳转循环中】
// 如果去的是白名单✨页面, 则放行
// includes 可以判断一个数组中是否包含某一个元素,并返回true 或者false
if (whiteList.includes(to.path)) {
next()
} else {
// 如果其他页面请强制拦截并跳转到登录页面
next('/login')
}
}
})
完整代码:
import Vue from 'vue'
import VueRouter from 'vue-router'
import $store from '@/store/index'
import layout from '@/views/layout/comLayout.vue' // 页面布局组件
Vue.use(VueRouter)
// 解决编程式路由往同一地址跳转时会报错的情况🍗
const originalPush = VueRouter.prototype.push
const originalReplace = VueRouter.prototype.replace
// push
VueRouter.prototype.push = function push(location, onResolve, onReject) {
// if (onResolve || onReject) {
// return originalPush.call(this, location, onResolve, onReject)
// }
return originalPush.call(this, location).catch((err) => err)
}
// replace
VueRouter.prototype.replace = function push(location, onResolve, onReject) {
// if (onResolve || onReject) {
// return originalReplace.call(this, location, onResolve, onReject)
// }
return originalReplace.call(this, location).catch((err) => err)
}
// 定义静态路由
const routes = [
{
path: '/',
isHidden: true, // 不在导航列表中显示
redirect: '/layout'
},
{
path: '/login',
isHidden: true, // 不在导航列表中显示
// 使用 路由✨懒加载
component: () => import('@/views/login/comLogin.vue')
},
{
path: '/register',
isHidden: true, // 不在导航列表中显示
component: () => import('@/views/register/comRegister.vue')
},
{
path: '/layout',
component: layout,
redirect: '/layout/home', // 当路由未匹配时重定向,可作初始化显示页面设置
children: [
{
path: 'home',
meta: {
title: '首页',
icon: ''
},
component: () => import('@/components/home.vue')
}
]
},
{
path: '/layout',
leaf: true, // 有二级路由
component: layout,
name: '/layout/zi',
meta: {
title: '个人中心',
icon: ''
},
children: [
{
path: 'user-info',
meta: {
title: '基本资料',
icon: ''
},
component: () => import('@/components/person-center/userInfo.vue')
},
{
path: 'user-avatar',
meta: {
title: '更换头像',
icon: ''
},
component: () => import('@/components/person-center/changePic.vue')
},
{
path: 'user-pwd',
meta: {
title: '重置密码',
icon: ''
},
component: () => import('@/components/person-center/resetPassword.vue')
}
]
}
]
const router = new VueRouter({
// 解决跳页面跳转后 新页面没有位于顶部的问题
scrollBehavior: () => ({ y: 0 }),
routes
})
// 定义白名单
const whiteList = ['/login', '/register']
// 定义404页面 对于用户自己手动的用输入链接访问路由表中不存在的路由组件,那么就直接提示无权访问.
const touter_404 = {
path: '*',
name: '404',
isHidden: true, // 不在导航列表中显示
component: () => import('@/views/error-page/404.vue')
}
// 记录路由 页面刷新之后这里会冲新定义,解决刷新页面动态路由丢失的问题
let hasRoles = true
// 发生页面跳转时触发
router.beforeEach(async (to, from, next) => {
const token = $store.state.token
// 权限访问控制
if (token) {
// 如果有token, 证明已登录
if (!$store.state.userInfo.username) {
// 有token但是没有用户信息, 才去请求用户信息保存到vuex里
// 调用actions里方法请求数据
$store.dispatch('initUserInfo') // 下次切换页面vuex里有用户信息数据就不会重复请求用户信息
await $store.dispatch('initRouter') // 获取路由信息
}
if (hasRoles) { // 添加动态路由
routerGo(to, next)
} else { // 没有动态路由的情况
next()
}
} else {
// 如果无token
// 【因为登录、注册页面也没有token,根据全局前置守卫的定义不做白名单处理,会一直处于跳转——触发——跳转循环中】
// 如果去的是白名单✨页面, 则放行
// includes 可以判断一个数组中是否包含某一个元素,并返回true 或者false
if (whiteList.includes(to.path)) {
next()
} else {
// 如果其他页面请强制拦截并跳转到登录页面
next('/login')
}
}
})
/**
* 添加路由
* @param {Object} to 要跳转的path
* @param {function} next 下一步
*
*/
function routerGo(to, next) {
const getRouter = filterAsyncRouter() // 过滤路由转换路由的 component
// 把404页面放到路由表的最后,避免动态路由刷新404
getRouter.push(touter_404)
// router.options.routes 静态路由
const routerArr = router.options.routes // 获取全部路由(静态加动态)
// 循环添加路由
getRouter.forEach(item => {
router.addRoute(item)
routerArr.push(item)
})
hasRoles = false // 添加完路由赋值为 false 下次不会再添加路由,页面刷新时又会变成true
// 把路由数据添加到Vuex 生成菜单
$store.commit('updateRouterArr', routerArr)
next({ ...to, replace: true })
}
function filterAsyncRouter() {
// 用深拷贝处理一下赋值操作
const router = JSON.parse(JSON.stringify($store.state.router)) // vuex 的路由信息
router.forEach(item => {
// 处理布局数组件的路径
item.component = () => import('@/views/layout/comLayout.vue')
if (item.children) {
item.children.forEach(it => {
const path = it.component
// webpack4的问题,不能直接使用字符变量作为引用,而且 it.component 不能直接写在路径里面
// 我也不知道原因直接写会找不到模块,必须用一个变量先接收,可能有其他方法我还不清楚
it.component = (resolve) => require([`@/components${path}.vue`], resolve)
})
}
})
return router
}
export default router
侧边导航栏的数据遍历,这里的 html结构是使用 element-ui 的
<!-- 侧边栏区域 -->
<el-aside width="200px">
<div class="user-box">
<img v-if="user_pic" :src="baseURL + user_pic" alt="" />
<img v-else src="../../assets/images/logo.png" alt="" />
<span>欢迎 {{ nickname || username }}</span>
</div>
<!-- 侧边栏导航部分 -->
<el-menu
:default-active="$route.path"
class="el-menu-vertical-demo"
@open="handleOpen"
@close="handleClose"
background-color="#23262E"
text-color="#fff"
active-text-color="#409EFF"
unique-opened
router
>
<!-- 外部嵌套template标签 -->
<!-- ① 进行数据的一次循环 -->
<template
v-for="item in newRouterArr || this.$router.options.routes"
>
<template v-if="!item.isHidden">
<!-- 一级菜单 -->
<el-menu-item
v-if="
!item.leaf && !item.leafThree && item.children.length === 1
"
:index="'/layout/' + item.children[0].path"
:key="'/layout/' + item.children[0].path"
>
<i :class="item.children[0].meta.icon"></i>
<span slot="title">{{ item.children[0].meta.title }}</span>
</el-menu-item>
<!-- 二级菜单 -->
<el-submenu
v-else-if="item.leaf"
:index="'/layout/' + item.meta.title"
:key="'/layout/' + item.meta.title"
>
<template slot="title">
<i :class="item.meta.icon"></i>
<span>{{ item.meta.title }}</span>
</template>
<!-- ② 进行数据的二次循环 -->
<el-menu-item
v-for="subItem in item.children"
:key="'/layout/' + subItem.path"
:index="'/layout/' + subItem.path"
>
<i :class="subItem.meta.icon"></i>{{ subItem.meta.title }}
</el-menu-item>
</el-submenu>
<!-- 三级菜单 -->
<el-submenu
:key="'/layout/' + item.indexPath"
:index="index + ''"
v-else-if="item.leafThree"
>
<template slot="title">
<div class="nav-first">
<i :class="item.meta.icon"></i>
<span>{{ item.meta.title }}</span>
</div>
</template>
<el-submenu
v-for="child in item.children"
:index="child.path"
:key="child.path"
>
<template v-if="child.leaf">
<span slot="title"
><span class="dot"></span>{{ child.meta.title }}</span
>
<el-menu-item
v-for="childThree in child.children"
:index="childThree.path"
:key="childThree.path"
>
<span>{{ childThree.meta.title }}</span>
</el-menu-item>
</template>
</el-submenu>
<el-menu-item
v-for="child in item.children"
:index="child.path"
:key="child.path"
>
<template v-if="!child.isHidden && !child.leaf">
<span slot="title"
><span class="dot"></span>{{ child.meta.title }}</span
>
</template>
</el-menu-item>
</el-submenu>
</template>
</template>
</el-menu>
</el-aside>