在現(xiàn)代 Web 應(yīng)用中,不同用戶擁有不同權(quán)限,這需要前端根據(jù)用戶角色動態(tài)控制路由和菜單的顯示。Vue3 提供了強大的動態(tài)路由機制,結(jié)合 Vite 和 Pinia,我們可以輕松構(gòu)建靈活且安全的權(quán)限系統(tǒng)。本文將詳細介紹如何實現(xiàn)動態(tài)路由,并提供一些實用技巧和優(yōu)化方案。
動態(tài)路由核心概念 動態(tài)路由的核心在于根據(jù)用戶權(quán)限實時生成路由配置。不同于靜態(tài)路由的固定配置,動態(tài)路由可以根據(jù)用戶的角色、權(quán)限等信息動態(tài)添加或移除路由規(guī)則,從而實現(xiàn)頁面級別的訪問控制。
實現(xiàn)步驟詳解 我們的 Vue3 項目中使用了 Vite 構(gòu)建工具和 Pinia 狀態(tài)管理庫。實現(xiàn)動態(tài)路由的關(guān)鍵步驟如下:
1. 后端接口設(shè)計: 后端接口需要根據(jù)用戶角色返回對應(yīng)的菜單權(quán)限數(shù)據(jù)。數(shù)據(jù)格式通常為 JSON,包含每個菜單項的 path
、name
、component
等信息。
2. 前端路由配置: 在前端路由文件 (src/router/index.ts
) 中,定義基礎(chǔ)路由和動態(tài)路由的入口。基礎(chǔ)路由通常包括登錄、注冊等無需權(quán)限校驗的頁面。動態(tài)路由入口則作為動態(tài)添加路由的占位符。
// src/router/index.ts import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' ;const routes : RouteRecordRaw [] = [ // 基礎(chǔ)路由 { path : '/login' , name : 'login' , component : () => import ('../views/Login.vue' ) }, // 動態(tài)路由入口 { path : '/dashboard' , name : 'dashboard' , component : () => import ('../views/Dashboard.vue' ), children : [], // 動態(tài)子路由將添加到這里 }, ];const router = createRouter ({ history : createWebHistory (), routes, });export default router;
3. 動態(tài)添加路由: 在用戶登錄成功后,獲取后端返回的菜單權(quán)限數(shù)據(jù)。將這些數(shù)據(jù)轉(zhuǎn)換為 RouteRecordRaw
格式,并使用 router.addRoute()
方法動態(tài)添加到路由實例中。
import { useUserStore } from '@/stores/user' ; router.beforeEach (async (to, from , next) => { const userStore = useUserStore (); if (to.name !== 'login' && !userStore.user ) { // 如果用戶未登錄,重定向到登錄頁 next ({ name : 'login' }); } else if (to.name !== 'login' && !router.hasRoute (to.name )) { // 動態(tài)添加路由 const menuData = await fetchUserMenu (); // 獲取菜單數(shù)據(jù) menuData.forEach (item => { router.addRoute ('dashboard' , { path : item.path , name : item.name , component : () => import (`../views/${item.component} .vue` ) }) }); // 確保 addRoute 完成后再跳轉(zhuǎn),避免出現(xiàn)空白頁面 next ({ ...to, replace : true }); // 跳轉(zhuǎn)到目標路由 } next (); });
4. Pinia 狀態(tài)管理: 使用 Pinia 存儲用戶的權(quán)限信息,方便在組件中進行權(quán)限控制。
// stores/user.ts import { defineStore } from 'pinia' ;export const useUserStore = defineStore ('user' , { state : () => ({ user : null , menu : [], }), actions : { async login (username, password ) { // 執(zhí)行登錄邏輯 ... this .user = user; this .menu = await fetchUserMenu (); // 獲取菜單數(shù)據(jù) }, logout () { this .user = null ; this .menu = []; // 移除動態(tài)添加的路由 const routeNames = this .menu .map (item => item.name ) routeNames.forEach (name => { if (router.hasRoute (name)) { router.removeRoute (name) } }) }, }, });
5. 菜單渲染: 在菜單組件中使用 Pinia 獲取用戶的菜單權(quán)限數(shù)據(jù),并根據(jù)數(shù)據(jù)動態(tài)渲染菜單項。
高級技巧與優(yōu)化 1. 401 無權(quán)限處理: 當(dāng)用戶訪問無權(quán)訪問的動態(tài)路由時,將其重定向到 401 頁面,而不是默認的 404 頁面,提供更友好的用戶提示。為此,我們創(chuàng)建了一個 getDynamicFullPathRoute
函數(shù),獲取所有動態(tài)路由的完整路徑,并在 beforeEach
中進行判斷。
2. 本地路由緩存(可選): 為了減少不必要的請求,可以將動態(tài)路由數(shù)據(jù)緩存在 localStorage
中。這樣,只有在用戶首次登錄或手動清除緩存時,才會重新請求路由數(shù)據(jù)。當(dāng)然,你需要根據(jù)實際需求權(quán)衡緩存策略。
// src/route/index.ts (部分代碼) import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router' import { useRoutesStore } from '~/stores/use-routes' // ... 其他代碼 router.beforeEach (async (to, from , next) => { // 401處理 (簡化示例) if (to.path === '/404' && to.redirectedFrom ?.fullPath ) { if (dynamicRoutes.includes (to.redirectedFrom .fullPath )) { next ({ name : '401' }) // 跳轉(zhuǎn)到401頁面 return } }// ... 其他邏輯 });// src/stores/use-routes.ts (部分代碼) import { defineStore } from 'pinia' import type { RouteRecordRaw } from 'vue-router' export const useRoutesStore = defineStore ('userRoutes' , { // ... 其他代碼 })
避免常見的坑 1. 逐個添加路由: 在 Vue3 中,需要使用 router.addRoute()
逐個添加動態(tài)路由。
2. 404 路由添加時機: 在所有動態(tài)路由添加完成后,再添加 404 路由。
3. 刷新頁面處理: 頁面刷新后,需要重新獲取動態(tài)路由,并在 next()
中使用 { ...to, replace: true }
避免導(dǎo)航錯誤。
總結(jié) 通過本文的講解,相信你已經(jīng)掌握了使用 Vue3、Vite 和 Pinia 實現(xiàn)動態(tài)路由的核心技巧。記住,權(quán)限系統(tǒng)的設(shè)計需要兼顧安全性和用戶體驗,動態(tài)路由為我們提供了一個優(yōu)雅的解決方案。
閱讀原文:原文鏈接 ?
該文章在 2024/12/30 15:29:49 編輯過