vue權(quán)限問題的完美解決方案
前言
最近一直在忙著一個用vue來做的權(quán)限管理的項目,其實在此之前,我也研究過vue的權(quán)限如何實現(xiàn),并且也為之寫過一篇博客,但當(dāng)真正應(yīng)用在項目中的時候,還是發(fā)現(xiàn)了許多問題,所以此篇也會就著我在項目中遇到的一些問題,拿出來和大家分享一下,當(dāng)然示例代碼還是我的github倉庫中的ant-design-vue-ms (本地下載)。
權(quán)限問題解決思路
對于一個前后端分離的項目而言,權(quán)限不再是僅僅靠后端來控制,后端只能控制接口的權(quán)限,前臺的頁面顯示還是需要我們來控制,針對vue的項目,首先我想的是當(dāng)權(quán)限不多,并且都是單個權(quán)限的情況下,我們完全沒有必要使用vue中提供的addRoutes的方法,可以使用動態(tài)組件來做,即我們根據(jù)后端返回的角色,來細(xì)度控制動態(tài)組件的顯示內(nèi)容,所謂動態(tài)組件其實就是vue內(nèi)置提供的component組件
<component :is="currentComponent"/>
相信看到這里,熟悉的同學(xué)應(yīng)該已經(jīng)想起來了,這樣的話,我們就不需要用到vuex,以及路由配置等等復(fù)雜的問題,單純靠后臺返回的角色名稱就能解決所有的問題了,看到這里是不是覺得今天的內(nèi)容就這些了,別著急,下面還有“好看的”。
權(quán)限設(shè)置中的問題
這樣雖然能解決一些簡單權(quán)限的問題,但是針對稍微復(fù)雜一些的權(quán)限應(yīng)用,就顯得有些力不從心了,當(dāng)角色過多,并且還包含了混合角色的權(quán)限的話,則會衍生出很多問題,這里也是列舉我遇到的一些問題,同學(xué)們可以細(xì)細(xì)推敲一下。
- 如果是混合角色的話,動態(tài)組件的路由跳轉(zhuǎn)實際都是跳轉(zhuǎn)到一個頁面,但是混合角色肯定會一個頁面中跳到不同角色的頁面,這樣可能我們要多寫很多層的判斷,權(quán)限混合越多,就越難以去判斷。
- 動態(tài)組件擴(kuò)展性比較差,如果我們再添加一個權(quán)限呢,就要再多加一個動態(tài)組件的內(nèi)容,并且出現(xiàn)混合權(quán)限的話,那改動的地方就更多了
所以綜上所述,最終我還是選擇了傳統(tǒng)的addRoutes,那么肯定會有同學(xué)問了,既然這個方案不行,那干嘛還要用呢。問得好,其實動態(tài)組件就是一種嘗試,只有知道錯了,不滿足需求了,我們才能更知道為什么會去使用傳統(tǒng)的addRoutes的權(quán)限方案。
權(quán)限問題解決方法
所以我們來看看addRoutes帶來的一些“好處”:
- 一次配置,多處使用,我們配置好了動態(tài)路由以后,不論后期添加多少權(quán)限,都能很好的顯示路由跳轉(zhuǎn)等等,并且也不需要改動代碼,只需要添加新增角色的模塊就可以了。
- 遇到混合角色的問題,如果內(nèi)容布局類似的話,我們可以使用自定義指令來區(qū)分要顯示的模塊,這樣的話如果一個賬號同時擁有很多角色的話,那么包含這個角色的模塊則會相應(yīng)的顯示出來,就不會出現(xiàn)需要判斷究竟顯示哪個模塊了,也不需要單獨為某個角色去設(shè)置一個頁面來顯示了。
相信做過權(quán)限的同學(xué)對上面的內(nèi)容還是有一些心得的,然后我們按照該有的步驟一步一步來,這些步驟在上面我的github中已經(jīng)有了,大家可以對照一下。
1、全局導(dǎo)航守衛(wèi)的設(shè)置,此處設(shè)置全局導(dǎo)航守衛(wèi),我覺得更多是為了數(shù)據(jù)持久化,大家都知道,vuex雖然非常好用,但是會有刷新丟失數(shù)據(jù)的情況,因此針對這種情況,我們使用導(dǎo)航守衛(wèi),每次刷新的時候,會重新請求后臺的接口來獲取角色信息。
if (store.getters.roles.length === 0) {
store
.dispatch('GetInfo')
.then(res => {
const roles = res.data.resultData && res.data.resultData.roles
store.dispatch('GenerateRoutes', { roles }).then(() => {
// 根據(jù)roles權(quán)限生成可訪問的路由表
// 動態(tài)添加可訪問路由表
router.addRoutes(store.getters.addRouters)
})
})
.catch(() => {
store.dispatch('Logout').then(() => {
next({ path: '/user/login', query: { redirect: to.fullPath } })
})
})
} else {
next()
}
這里代碼做了簡化,主要給大家看下上面會有一個角色判斷長度,主要是當(dāng)我們不刷新的情況,頁面角色信息不回丟失,因此我們也就沒有必要去請求后臺獲取角色信息了,來節(jié)省請求數(shù)量。
2. 通過上面的代碼可以看到,我們首先是請求的角色信息,然后請求了生成路由的GenerateRoutes的方法,方法是寫在vuex中的action里面的,這部分的內(nèi)容因為網(wǎng)上有很多教程,其實主要歸納一下,就是對路由進(jìn)行遞歸過濾,過濾出符合角色的路由,然后將靜態(tài)路由和過濾出來的動態(tài)路由鏈接起來
const permission = {
state: {
routers: constRouterMap,
addRouters: []
},
mutations: {
SET_ROUTERS: (state, routers) => {
state.addRouters = routers
state.routers = constRouterMap.concat(routers)
}
},
actions: {
GenerateRoutes({ commit }, data) {
//略
}
}
}
3、設(shè)置我們的路由文件,這部分放到這里來說,主要因為這里還有個小坑,所以也是特地拿出來和大家分享一下
export const constRouterMap = [
{
path: '/',
redirect: '/index',
component: BasicLayout,
children: [
{
path: '/index',
name: 'index',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '@/views/About.vue'),
meta: {
title: '儀表盤'
}
},
{
path: '/home',
name: 'home',
component: () => import(/* webpackChunkName: "home" */ '@/views/Home.vue'),
meta: {
title: '表單頁'
}
},
{
path: '/pattern',
name: 'pattern',
component: () => import(/* webpackChunkName: "pattern" */ '@/views/DesignPattern.vue')
},
{
path: '/map',
name: 'map',
component: () => import(/* webpackChunkName: "map" */ '@/views/DataMap.vue'),
meta: {
title: '地圖組件'
}
},
]
},
{
path: '/user',
redirect: '/login',
component: UserLayout,
children: [
{
path: '/login',
name: 'login',
component: () => import(/* webpackChunkName: "login" */ '@/views/user/Login.vue')
},
{
path: '/register',
name: 'register',
component: () => import(/* webpackChunkName: "login" */ '@/views/user/Register.vue')
}
]
},
//需要注意這里,404的路由一定要寫在靜態(tài)路由中
{
path: '/404',
component: () => import(/* webpackChunkName: "not_found" */ '@/views/NotFound.vue')
}
]
export const asyncRouterMap = [
{
path: '/',
redirect: '/index',
component: BasicLayout,
children: [
{
path: '/controls',
name: 'controls',
component: () => import(/* webpackChunkName: "controls" */ '@/views/Controls.vue'),
meta: {
title: '權(quán)限設(shè)置',
permission: ['admin']
}
}
]
},
//捕獲未定義的路由配置
{
path: '*',
redirect: '/404',
hidden: true
}
]
上面關(guān)于404頁面的定義順序非常重要,如果在靜態(tài)路由中定義了捕獲的路由path:"*",而在動態(tài)路由中定義了404路由的話,則當(dāng)導(dǎo)航鉤子中判斷比較復(fù)雜的話,會出現(xiàn)一些意想不到的錯誤,我就是當(dāng)時寫反了順序,并且還在導(dǎo)航鉤子中做了一些復(fù)雜的面包屑的判斷,一旦刷新頁面的話,則會出現(xiàn)以下錯誤

這種錯誤的產(chǎn)生,可能是因為刷新時,導(dǎo)航鉤子發(fā)現(xiàn)動態(tài)添加進(jìn)來的路由找不到一直進(jìn)行獲取動態(tài)路由的方法,導(dǎo)致最后調(diào)用棧溢出所導(dǎo)致,因此大家在使用的時候一定要非常小心。
4. 當(dāng)我們生成路由后,退出應(yīng)用的切換新的角色賬號進(jìn)行登錄時,一定要記得的兩件事,第一就是清空vuex里面的角色信息,在不刷新的情況下,這些信息是不會丟失的,當(dāng)不同角色的賬號登錄時,原來的角色依然存在,那么肯定會出現(xiàn)問題,其次則是在跳轉(zhuǎn)會登錄頁的時候,需要設(shè)置刷新頁面的代碼
window.location.reload();
this.$router.push({name: 'login'});
先刷新以后再跳轉(zhuǎn)到登錄頁,這個則是因為addRoutes生成的路由在不刷新的情況下會一直存在,即使下個不同角色的賬號登錄時,依然會拿之前存在的路由信息去進(jìn)行過濾,這樣過濾的結(jié)果必然是當(dāng)前角色的路由一個都不存在,因此生成的路由信息還是上個角色的路由,所以在完成了之前這些步驟時,一定不要忘記了做這兩步,這樣也才是一個完整的權(quán)限解決方案
尾聲
以上也是我在項目中一些收貨吧,拿出來和大家分享,也是希望大家少走一些彎路,留心我們開發(fā)中遇到的每個看似很小的問題,其實往往是我們最后解決問題的關(guān)鍵,不論是從動態(tài)組件還是動態(tài)路由,問題的出現(xiàn)也是我們不斷去完善自己的過程。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
相關(guān)文章
解決vue中post方式提交數(shù)據(jù)后臺無法接收的問題
今天小編就為大家分享一篇解決vue中post方式提交數(shù)據(jù)后臺無法接收的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08
Vue3監(jiān)聽屬性與Computed的區(qū)別詳解
在 Vue 3 中,watch 和 computed 都是非常重要的概念,它們都可以用于觀察和響應(yīng)數(shù)據(jù)的變化,但在使用場景和原理上存在明顯的區(qū)別,本文將詳細(xì)解析 Vue 3 中監(jiān)聽屬性 (watch) 和計算屬性 (computed) 的區(qū)別,需要的朋友可以參考下2024-02-02
vue中el-date-picker選擇日期的限制的項目實踐
ElementUI的el-date-picker使用時,有時候想要限制用戶選擇的時間范圍,本文就來介紹了vue中el-date-picker選擇日期的限制的項目實踐,感興趣的可以了解一下2023-10-10
vue中el-table實現(xiàn)自動吸頂效果(支持fixed)
本文主要介紹了vue中el-table實現(xiàn)自動吸頂效果,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09

