思路
1.登录获取用户所拥有菜单权限
2.促发action暂存菜单信息
3.创建全局路由前置守卫
4.合理利用next
1.登录获取路由信息
- 一、接口返回数据:
[
{
"path":"/system",
"name":"System",
"children":[
{
"path":"/system/userManagement",
"name":"UserManagement",
"component":"() => import("../views/system/userManagement/index.vue"
},
{
"path":"/system/menuManagement",
"name":"MenuManagement",
"component":"() => import("../views/system/menuManagement/index.vue"
}
]
}
]
- 二、store/index.ts
import { defineStore } from 'pinia'
import { addRoute } from '@/router/index'
// 路由状态管理
export const useLoginSettingStore = defineStore('loginSettingStore', {
state: () => ({
USER_ROUTES: JSON.parse(localItem.getItem('USER_ROUTES')) || [],
}),
actions: {
setRoutes() {
return request({
url: '/api/routers',
}).then((res: any) => {
this.USER_ROUTES = res
localStorage.setItem('USER_ROUTES', JSON.stringify(res))
addRoute(res)
})
},
},
})
- 二、login.vue - 接口调用获取登录信息
import { useLoginSettingStore } from '@/store/useLoginSetting'
const { setRoutes } = useLoginSettingStore()
// 促发actions
setRoutes().then(() => {
router.replace(/system)
})
- 三、静态路由配置
router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: Array<RouteRecordRaw> = [
{
path: '/',
name: 'Home',
component: () => import('@/views/Home.vue')
},
{
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue')
},
{
path: '/404',
name: '404',
component: () => import('@/views/404.vue'),
meta: {},
},
{
path: '/:pathMatch(.*)',
redirect: '/404',
meta: {},
},
{
path: '/:catchAll(.*)',
redirect: '/404',
meta: {},
},
]
const router = createRouter({
history: createWebHistory(),
routes,
})
// Vite 支持使用特殊的 import.meta.glob 函数从文件系统导入多个模块:
const _modules = import.meta.glob('../views/**/**.vue')
const initRouter = (routers: any) => {
if (!routers) return []
routers.forEach((route: any) => {
if (route.component) {
route.component = _modules[route.component]
}
if (route.children != undefined && route.children.length > 0) {
initRouter(route.children)
}
})
return routers
}
export const addRoute = (routes: any) => {
const routers = initRouter(routes)
routers.forEach(async (route: any) => {
if (!router.hasRoute(route.name)) {
router.addRoute(route)
}
})
}
注: 在Vue3中,废弃了 addRoutes() 方法,只保留了 addRoute() 单个添加路由配置的方法。
- 四、路由守卫
router.beforeEach((to: any, from: any, next: any) => {
let token = localStorage.getItem('token')
let routes = localStorage.getItem('USER_ROUTES')
let isLoadRouters = true
if (!token) {
next(‘/login’)
}
if (to.path == '/login') {
next()
}
// 没有路由信息时,需重新请求
if (routes.length === 0) {
setRoutes().then(() => {
next({ ...to, replace: true })
})
} else {
// 判断是否已添加路由
if (isLoadRouters) {
// 添加动态路由
addRoute(routes)
isLoadRouters = false
// 解决刷新页面空白
next({ ...to, replace: true })
} else {
// 路由已添加,可直接跳转
next()
}
}
})
注:
每一次导航路由的跳转,都会经过一遍路由全局守卫,如果直接去使用next(路径),就会不断进行路由判断,进入无限循环
通过addRoute动态添加的路由如果刚添加完就立刻放行,此时addRoute还没有执行结束,因此找不到刚添加的路由,就会导致白屏,因此需要重新访问一遍路由才行
next({…to})能够递归调用beforeEach直到找到对应的路由。
replace:true是防止在递归调用期间用户点击浏览器的后退按钮产生错误。