手把手教學(xué)vue的路由權(quán)限問題
后臺管理類系統(tǒng)大多都涉及權(quán)限管理,菜單權(quán)限,按鈕權(quán)限。
菜單權(quán)限
菜單權(quán)限對應(yīng) - 路由。菜單權(quán)限 - 根據(jù)用戶角色不同,路由文件動態(tài)配置。
相關(guān)知識點了解
vue-router是vue項目在進(jìn)行開發(fā)過程中必不可少缺少的插件,目前vue2依賴的是vue-router3,vue3依賴的vue-router4
在進(jìn)行權(quán)限控制之前一定要了解哪些路由需要權(quán)限哪些不需要
知識點集結(jié)
router.addRoutes()
動態(tài)添加更多的路由規(guī)則。參數(shù)必須是一個符合 routes
選項要求的數(shù)組。
已廢棄目前版本再使用該api會被提示已經(jīng)廢棄,但是暫時依舊可以使用
router.addRoutes(routes: Array<RouteConfig>)
router.addRoute()
添加一條新路由規(guī)則。如果該路由規(guī)則有 name
,并且已經(jīng)存在一個與之相同的名字,則會覆蓋它。
addRoute(route: RouteConfig)
router.getRoutes()
獲取所有活躍的路由記錄列表。注意只有文檔中記錄下來的 property 才被視為公共 API,避免使用任何其它 property,例如 regex
,因為它在 Vue Router 4 中不存在。
路由導(dǎo)航守衛(wèi) - beforeEach
router.beforeEach((to, from, next) => { /* 必須調(diào)用 `next` */ })
全局前置守衛(wèi) - 跳轉(zhuǎn)一個路由之前都會執(zhí)行.
3個參數(shù):
to
: 即將進(jìn)入的目標(biāo)的路由對象from
:當(dāng)前導(dǎo)航正在離開的路由next
: 是個函數(shù),進(jìn)入下一個鉤子
next()
: 進(jìn)行管道中的下一個鉤子。如果全部鉤子執(zhí)行完了,則導(dǎo)航的狀態(tài)就是 confirmed (確認(rèn)的)。next(false)
: 中斷當(dāng)前的導(dǎo)航。如果瀏覽器的 URL 改變了 (可能是用戶手動或者瀏覽器后退按鈕),那么 URL 地址會重置到 from 路由對應(yīng)的地址。next('/')
或者next({ path: '/' })
: 跳轉(zhuǎn)到一個不同的地址。當(dāng)前的導(dǎo)航被中斷,然后進(jìn)行一個新的導(dǎo)航。你可以向 next 傳遞任意位置對象,且允許設(shè)置諸如 replace: true、name: 'home' 之類的選項以及任何用在 router-link 的 to prop 或 router.push 中的選項。next(error)
: (2.4.0+) 如果傳入 next 的參數(shù)是一個 Error 實例,則導(dǎo)航會被終止且該錯誤會被傳遞給 router.onError() 注冊過的回調(diào)。
功能實現(xiàn)過程
路由權(quán)限第一種可以是后端全部返回,直接調(diào)用接口使用后端的可以使用的路由,但是這種情況一般較少。
第二種前端有一個份完整路由,根據(jù)后端接口進(jìn)行篩選,這里講解第二種情況。
(1)定義路由文件
router -> index.js
import Vue from 'vue' import Router from 'vue-router' import store from '../store' Vue.use(Router) //沒有權(quán)限的路由 export const constantRoutes = [ { path: '/', name: 'index', redirect: '/login' }, { path: '/login', name: 'login', component: () => import('@/views/login') }, { path: '/register', name: 'register', component: () => import('@/views/register') }, { path: '/forget', name: 'forget', component: () => import('@/views/forget') }, { path: '/404', name: 'notfing', component: () => import('@/views/404')} ] //有權(quán)限的路由 export const myAsyncRoute = [{ path: '/portal', name: 'portal', component: LayoutPortal, redirect: '/portal/home', meta: {id: 'no'}, children: [ { path: '/portal/home', name: 'portal-home', component: () => import('@/views/portal/home/index.vue'), meta: {id: 100100, title: '首頁', show: true} }, { path: '/portal/user', name: 'portal-user', component: () => import('@/views/layout/submenu'), meta: {id: 100200, title: '統(tǒng)一身份認(rèn)證', show: true}, redirect: '/portal/user/userInfo', children: [ { path: '/portal/user/userInfo', name: 'portal-user-userInfo', component: () => import('@/views/portal/userInfo/index.vue'), meta: {id: 100201, title: '統(tǒng)一用戶管理', show: true} }, { path: '/portal/user/userInfo/detial', name: 'portal-userInfo-detial', component: () => import('@/views/portal/userInfo/detial.vue'), meta: { id: 100201, activeMenu: '/portal/user/userInfo', title: '統(tǒng)一用戶管理', show: false}, }, { path: '/portal/user/userAuth', name: 'portal-user-userAuth', component: () => import('@/views/portal/userAuth/index.vue'), meta: {id: 100202, title: '用戶認(rèn)證', show: true} }, ] }, { path: '/portal/journal', name: 'portal-journal', component: () => import('@/views/portal/journal/index.vue'), meta: {id: 100303, title: '統(tǒng)一日志管理', show: true}, }, { path: 'personal', name: 'portal-personal', component: () => import('@/views/portal/personal/index.vue'), meta: { id: 'no', activeMenu: '/portal/home', show: false }, }, ], }] export default new Router({ routes: constantRoutes, })
(2)注冊路由
main.js
import router from './router' new Vue({ el: '#app', router, store, components: { App }, template: '<App/>' })
正常注冊完路由就可以開始進(jìn)行權(quán)限設(shè)置了
(3)獲取有權(quán)限路由
不同的項目獲取路由權(quán)限并不相同,大多數(shù) - 登錄接口返回/單獨接口進(jìn)行獲取
登錄獲取權(quán)限保存
let res = await this.$api.user.login(data); if(res.token) { this.$store.commit('setToken', res.token); this.$store.commit('setUsername', res.nickName); this.$store.commit('setUserInfo', res); //存儲權(quán)限 localStorage.setItem('menuIdList', JSON.stringify(res.menuIdList)) this.$message({ message: '登錄成功', type: 'success' }); setTimeout(() => { this.loading = false; this.$router.push('/portal') }, 1000); }
在main.js中攔截
獲取權(quán)限進(jìn)行匹配 - beforeEach一定要有一個next()的出口,不然會陷入死循環(huán)
let flag = true; router.beforeEach((to, from, next) => { if (['/login', '/forget', '/register'].includes(to.path)) { next(); } else { let token = localStorage.getItem("token"); if (token) { if(flag) { try { //獲取有權(quán)限的路由進(jìn)行組裝 let route = asyncRoute() || []; router.addRoutes(route) router.addRoute({ path: '*', redirect: '/404' }) flag = false next({...to, replace:true}) }catch(e) { next('/login') } }else { next(); } }else { next({ path: '/login' }) } } } );
注意: addRoute之后,打印router是看不見的,要獲取所有的權(quán)限,必須使用router.getRoute()進(jìn)行查看
(4)路由權(quán)限匹配
router.js-根據(jù)后端返回的權(quán)限,和自己的路由權(quán)限匹配,組織成新的路由,和自己想要的格式
export const asyncRoute = function() { let menuIdList = localStorage.getItem('menuIdList') ? JSON.parse(localStorage.getItem('menuIdList')) : []; let tempArr = filterRoute(myAsyncRoute, menuIdList); store.dispatch('getRoute', tempArr) let showRoute = []; let nowRoute =JSON.parse(JSON.stringify(tempArr)) if(nowRoute[0].children && nowRoute[0].children.length){ nowRoute[0].children.forEach(item => { let arr = []; if(item.children && item.children.length){ arr = item.children.filter(obj => obj.meta.show) } if(item.meta.show){ item['showRouteChildren'] = arr; showRoute.push(item) } }) } store.dispatch('getShowRoute', showRoute) return tempArr } function filterRoute(arr, menuIdList) { if(!arr.length) return []; return arr.filter(item => { if(item.children && item.children.length) { item.children = filterRoute(item.children, menuIdList); } return (item.meta && item.meta.id && menuIdList.includes(item.meta.id)) || (item.meta && item.meta.id == 'no') || (item.children && item.children.length > 0) }) }
在這個過程中,在store存儲了路由
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export default new Vuex.Store({ state: { routes: JSON.parse(localStorage.getItem('routes')) || [], addRoutes: JSON.parse(localStorage.getItem('addRoutes')) ||[], showRoutes: [] }, mutations: { setRoutes(state, routes) { state.addRoutes = routes; state.routes = constantRoutes.concat(routes) localStorage.setItem('routes', JSON.stringify(state.routes)) localStorage.setItem('addRoutes', JSON.stringify(state.addRoutes)) }, setShowRoutes(state, routes) { state.showRoutes = routes; } }, actions: { getRoute({commit}, list) { return new Promise(resolve => { commit('setRoutes', list) resolve(list) }) }, getShowRoute({commit}, list) { return new Promise(resolve => { commit('setShowRoutes', list) resolve(list) }) } } })
總結(jié): 最后在理一下整個過程 - 存儲權(quán)限路由數(shù)據(jù),在進(jìn)行跳轉(zhuǎn)的時候進(jìn)行篩選組合路由。其實篩選路由不一定要寫在router.js,只要是你認(rèn)為合適的地方都可以,在權(quán)限控制過程中最重要的是路由攔截。
模擬一下路由攔截的過程
假設(shè)login之后進(jìn)入的/index,路由攔截的過程
/index - > token -> flag(true) ->獲取路由權(quán)限 -> next('/index') -> 重新進(jìn)入beforeEach-> token->flag(false) -> next() ->結(jié)束
按鈕權(quán)限 - 操作(自定義指令)
按鈕權(quán)限主要涉及的知識點就是全局自定義指令
寫在main.js。或者單獨js文件,main.js進(jìn)行引入
import Vue from "vue" import store from "../store" //自定義指令 v-has進(jìn)行權(quán)限判斷 Vue.directive("has",{ inserted : function (el,binding){ //按鈕權(quán)限 const data = store.state.buttons; //按鈕的值 <el-button v-has>aa</el-button> const value = binding.value; //a const hasPermissions = data.includes(value); if(!hasPermissions){ //隱藏按鈕 el.style.display = "none"; setTimeout(()=>{ el.parentNode.removeChild(el) },0) } } })
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Vue自定義復(fù)制指令 v-copy功能的實現(xiàn)
這篇文章主要介紹了Vue自定義復(fù)制指令 v-copy,使用自定義指令創(chuàng)建一個點擊復(fù)制文本功能,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-01-01vue使用axios?post發(fā)送json數(shù)據(jù)跨域請求403的解決方案
這篇文章主要介紹了vue使用axios?post發(fā)送json數(shù)據(jù)跨域請求403的解決方案,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-12-12vue.config.js中devServer.proxy配置說明及配置正確不生效問題解決
Vue項目devServer.proxy代理配置詳解的是一個非常常見的需求,下面這篇文章主要給大家介紹了關(guān)于vue.config.js中devServer.proxy配置說明及配置正確不生效問題解決的相關(guān)資料,需要的朋友可以參考下2023-02-02vue-element-admin 菜單標(biāo)簽失效的解決方式
今天小編就為大家分享一篇vue-element-admin 菜單標(biāo)簽失效的解決方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-11-11Vue Vine實現(xiàn)一個文件中寫多個組件的方法(最近很火)
Vue Vine提供了全新Vue組件書寫方式,主要的賣點是可以在一個文件里面寫多個vue組件,Vue Vine是一個vite插件,vite解析每個模塊時都會觸發(fā)插件的transform鉤子函數(shù),本文介紹Vue Vine是如何實現(xiàn)一個文件中寫多個組件,感興趣的朋友一起看看吧2024-07-07Vue+axios 實現(xiàn)http攔截及路由攔截實例
這篇文章主要介紹了Vue+axios 實現(xiàn)http攔截及路由攔截 ,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-04-04