前言
最近在用vue3及相关生态完成自己一个小的博客网站搭建来学习时,想到以前开发中遇到菜单生成是由后端返回数据动态生成的,但是这块逻辑不是自己写的,所以理解不深刻。趁着这次学习vue3时准备实操一波。
一、用户权限和菜单列表数据
在store文件夹下新建menu.ts文件,引入pinia(vue3推荐的全局存储,和vuex类似)创建store存储菜单数据以及定义生成动态路由的方法,通过addRoute添加动态路由
import { ref, reactive, computed } from 'vue'
import { defineStore } from 'pinia'
import type { TSideMenu } from '@/type'
import router from '../router/index'
import {compMoudles} from '@/router/compModule'
export const useMenuStore = defineStore('Menu', () => {
// 返回的菜单数据
let rootMenu = ref([] as TSideMenu[])
// 侧边栏菜单数据
let sideMenu = ref([] as TSideMenu[])
function setRootMenu(menu:TSideMenu[]) {
rootMenu.value = menu
addRouters(menu)
if(rootMenu.value.length>0){
sideMenu.value = rootMenu.value[0].children
}
}
function addRouters(menu:TSideMenu[]){
for (let index = 0; index < menu.length; index++) {
let menuItem = menu[index];
if(menuItem.children.length>0){
addRouters(menuItem.children)
}else{
console.log(menuItem,'menuItem')
router.addRoute("Home",
{ path: menuItem.menuUrlParam?`${menuItem.menuUrl}${menuItem.menuUrlParam}`:menuItem.menuUrl as string,
component: compMoudles[menuItem.menuCode],
children: [],
})
}
}
}
function setSideMenu(menu:TSideMenu[]) {
sideMenu.value = [...menu]
}
return { sideMenu,rootMenu, setRootMenu,setSideMenu}
})
二、设置动态路由
1.在router下新建compModule.ts
该文件中定义动态路由对应的vue文件。在addRouter中添加component时根据menuCode引入
export const compMoudles = {
kweichowMt:()=>import('../views/personCenter/myCollection.vue'),
vueBlog:()=>import('../views/article/index.vue'),
addBlog:()=>import('../views/article/addBlog.vue'),
blogList:()=>import('../views/article/blogList.vue')
}
2.在router下新建index.ts文件
在index.ts中定义默认的静态路由以及全局路由守卫做鉴权处理,我这里还有相关功能需完善。
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router'
import LoginView from '../views/login/index.vue'
import MainIndex from '@/views/mainIndex/index.vue'
import layout from '@/views/layout/index.vue'
import {useUserStore} from '@/stores/user'
import { useMenuStore } from '@/stores/menu'
import {getMenuByRole} from './routerUtils'
const baseRoutes = [
{
path: '/login',
name: 'login',
component: LoginView
},
{
path: '/',
component: layout,
redict:'/home',
name:'Home',
children: [
{ path: "/home", component: MainIndex, name:'MainIndex',
meta:{
title:'首页'
}
},
],
},
]
const router = createRouter({
history: createWebHashHistory(),
routes: baseRoutes,
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { top: 0 }
}
}
})
router.beforeEach(async(to, from) => {
const ustore = useUserStore()
const mstore = useMenuStore()
// 未登陆跳转到登陆页 store的局限性 刷新后丢失存储
if(to.path =='/login'){
return true
}
if(!ustore.token && to.path!='/login'){
return '/login'
} else {
// debugger
// 已登录已加载菜单 直接next
if(mstore.rootMenu.length>0) {
return true
} else {
//已登录未加载菜单 根据角色请求菜单
const res= await getMenuByRole(ustore.userRole)
if(res){
// 动态注册路由
mstore.setRootMenu(res.data.resultList)
}
return to.fullPath
}
}
})
export default router
因为vueRouter的路由守卫不推荐next,所以我使用return,但是在该处一开始我是将return放在异步函数的回调中,造成页面刷新一直白屏,才恍然大悟,return一定要在路由守卫的函数中,但是要保证动态路由已生成,所以使用await,在外层使用return进行重定向即可。