在后臺(tái)管理系統(tǒng)中,前端的路由往往需要根據(jù)用戶的權(quán)限動(dòng)態(tài)生成。這篇文章將重點(diǎn)介紹如何在 Vue 3 中實(shí)現(xiàn)動(dòng)態(tài)路由注冊(cè)和手動(dòng)導(dǎo)航,確保用戶訪問的頁面與權(quán)限對(duì)應(yīng)。
1. 動(dòng)態(tài)路由的需求與原理
為什么需要?jiǎng)討B(tài)路由?
- 權(quán)限控制:不同用戶角色需要看到不同的菜單和頁面。
- 后端驅(qū)動(dòng):后端返回菜單數(shù)據(jù),前端動(dòng)態(tài)渲染菜單和注冊(cè)路由。
- 避免硬編碼:路由不再寫死在前端代碼里,保證靈活性。
- 靜態(tài)路由:定義公共頁面,如登錄頁和404頁面。
- 動(dòng)態(tài)路由:存儲(chǔ)需要通過后端數(shù)據(jù)動(dòng)態(tài)添加的頁面。
- 動(dòng)態(tài)注冊(cè)路由:根據(jù)后端返回的數(shù)據(jù),通過router.addRoute動(dòng)態(tài)添加到路由系統(tǒng)。
- 手動(dòng)導(dǎo)航:添加新路由后,需要手動(dòng)觸發(fā)導(dǎo)航保證路由生效。
2.靜態(tài)路由與動(dòng)態(tài)路由配置靜態(tài)路由是所有用戶共享的基本路由,如登錄頁、404頁等。import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router';import Layout from '@/layout/admin.vue';
const routes: Array<RouteRecordRaw> = [ { path: '/', component: Layout, name: 'admin', }, { path: '/login', name: 'login', component: () => import('@/pages/login/index.vue'), meta: { title: '登錄頁' }, }, { path: '/:pathMatch(.*)*', name: 'NotFound', component: () => import('@/pages/404/index.vue'), meta: { title: '404' }, },];
動(dòng)態(tài)路由需要根據(jù)后端返回的數(shù)據(jù)進(jìn)行注冊(cè):const asyncRoutes: Array<RouteRecordRaw> = [ { path: '/', name: '/', component: () => import('@/pages/index/index.vue'), meta: { title: '首頁' }, }, { path: '/goods/list', name: '/goods/list', component: () => import('@/pages/goods/list.vue'), meta: { title: '商品列表' }, }, { path: '/category/list', name: '/category/list', component: () => import('@/pages/category/list.vue'), meta: { title: '分類列表' }, },];
3.addRoutes實(shí)現(xiàn)動(dòng)態(tài)路由注冊(cè)該方法接收后端返回的菜單數(shù)組,并將匹配的動(dòng)態(tài)路由添加到主路由admin下。import { router } from '@/router/index';
export const addRoutes = (routesArray: Array<MenusArray>) => { let hasNewRoutes = false;
const addRoutesFromMenus = (menus: Array<MenusArray>) => { menus.forEach((menu) => { const route = asyncRoutes.find((r) => r.path === menu.frontpath); // 添加到router中 if (route && !router.hasRoute(route.path)) { router.addRoute('admin', route); hasNewRoutes = true; } // 遞歸處理子菜單 if (menu.child && menu.child.length > 0) { addRoutesFromMenus(menu.child); } }); };
addRoutesFromMenus(routesArray); return hasNewRoutes;};
- router.addRoute:VueRouter提供的API,可以動(dòng)態(tài)添加路由。
- 避免重復(fù)添加:router.hasRoute檢查路由是否已存在,防止重復(fù)注冊(cè)。
- 遞歸處理:支持多級(jí)菜單,遞歸遍歷child子菜單。
4.路由守衛(wèi)中調(diào)用addRoutes在router.beforeEach路由守衛(wèi)中,調(diào)用addRoutes注冊(cè)動(dòng)態(tài)路由,并實(shí)現(xiàn)手動(dòng)導(dǎo)航。import { getToken } from '@/utils/auth';import store from '@/store';import { addRoutes } from '@/router/index';
router.beforeEach(async (to, from, next) => { const token = getToken(); let hasNewRoutes = false;
// 顯示全局加載狀態(tài) showFullLoading();
// 未登錄跳轉(zhuǎn)到登錄頁 if (!token && to.name !== 'login' && to.name !== 'NotFound') { return next('/login'); }
// 已登錄訪問登錄頁,重定向到主頁 if (token && to.name === 'login') { return next('/'); }
// 已登錄狀態(tài)下,動(dòng)態(tài)添加路由 if (token) { await store.dispatch('user/getUserInfo'); // 拉取用戶信息 hasNewRoutes = addRoutes(store.getters['menu/getMenus']); // 添加動(dòng)態(tài)路由 }
// 設(shè)置頁面標(biāo)題 if (to.meta.title) { document.title = `${to.meta.title}-測(cè)試后臺(tái)管理`; } else { document.title = '測(cè)試后臺(tái)管理'; }
// 手動(dòng)導(dǎo)航:如果添加了新路由,重新跳轉(zhuǎn)當(dāng)前頁面 hasNewRoutes ? next(to.fullPath) : next();});
- router.addRoute是動(dòng)態(tài)操作,添加新路由后需要重新跳轉(zhuǎn)一次,確保用戶能正常訪問新注冊(cè)的頁面。
- next(to.fullPath):手動(dòng)跳轉(zhuǎn)到當(dāng)前頁面。
- 獲取菜單數(shù)據(jù):后端返回用戶權(quán)限對(duì)應(yīng)的菜單數(shù)據(jù)。
- 動(dòng)態(tài)注冊(cè)路由:調(diào)用
addRoutes 將菜單數(shù)據(jù)匹配的路由添加到 admin 下。 - 手動(dòng)導(dǎo)航:動(dòng)態(tài)路由添加完成后,使用
next(to.fullPath) 手動(dòng)觸發(fā)頁面跳轉(zhuǎn)。 - 權(quán)限生效:用戶只能訪問與自己權(quán)限匹配的頁面。
- 動(dòng)態(tài)路由:后端驅(qū)動(dòng),通過
addRoutes 動(dòng)態(tài)注冊(cè)。 - 手動(dòng)導(dǎo)航:解決動(dòng)態(tài)添加路由后無法直接訪問的問題。
- 靈活性:動(dòng)態(tài)路由使前端代碼更靈活,后端控制權(quán)限更方便。
閱讀原文:原文鏈接
該文章在 2024/12/30 16:01:11 編輯過