動(dòng)態(tài)加載權(quán)限管理模塊中的Vue組件
本文我們主要來(lái)聊聊登錄以及組件的動(dòng)態(tài)加載。
登錄狀態(tài)保存
當(dāng)用戶登錄成功之后,需要將當(dāng)前用戶的登錄信息保存在本地,方便后面使用。具體實(shí)現(xiàn)如下:
登錄成功保存數(shù)據(jù)
在登錄操作執(zhí)行成功之后,通過(guò)commit操作將數(shù)據(jù)提交到store中,核心代碼如下:
this.postRequest('/login', {
username: this.loginForm.username,
password: this.loginForm.password
}).then(resp=> {
if (resp && resp.status == 200) {
var data = resp.data;
_this.$store.commit('login', data.msg);
var path = _this.$route.query.redirect;
_this.$router.replace({path: path == '/' || path == undefined ? '/home' : path});
}
});
store
store的核心代碼如下:
export default new Vuex.Store({
state: {
user: {
name: window.localStorage.getItem('user' || '[]') == null ? '未登錄' : JSON.parse(window.localStorage.getItem('user' || '[]')).name,
userface: window.localStorage.getItem('user' || '[]') == null ? '' : JSON.parse(window.localStorage.getItem('user' || '[]')).userface
}
},
mutations: {
login(state, user){
state.user = user;
window.localStorage.setItem('user', JSON.stringify(user));
},
logout(state){
window.localStorage.removeItem('user');
}
}
});
為了減少麻煩,用戶登錄成功后的數(shù)據(jù)將被保存在localStorage中(防止用戶按F5刷新之后數(shù)據(jù)丟失),以字符串的形式存入,取的時(shí)候再轉(zhuǎn)為json。當(dāng)用戶注銷登陸時(shí),將localStorage中的數(shù)據(jù)清除。
組件動(dòng)態(tài)加載
在權(quán)限管理模塊中,這算是前端的核心了。
核心思路
用戶在登錄成功之后,進(jìn)入home主頁(yè)之前,向服務(wù)端發(fā)送請(qǐng)求,要求獲取當(dāng)前的菜單信息和組件信息,服務(wù)端根據(jù)當(dāng)前用戶所具備的角色,以及角色所對(duì)應(yīng)的資源,返回一個(gè)json字符串,格式如下:
[
{
"id": 2,
"path": "/home",
"component": "Home",
"name": "員工資料",
"iconCls": "fa fa-user-circle-o",
"children": [
{
"id": null,
"path": "/emp/basic",
"component": "EmpBasic",
"name": "基本資料",
"iconCls": null,
"children": [],
"meta": {
"keepAlive": false,
"requireAuth": true
}
},
{
"id": null,
"path": "/emp/adv",
"component": "EmpAdv",
"name": "高級(jí)資料",
"iconCls": null,
"children": [],
"meta": {
"keepAlive": false,
"requireAuth": true
}
}
],
"meta": {
"keepAlive": false,
"requireAuth": true
}
}
]
前端在拿到這個(gè)字符串之后,做兩件事:1.將json動(dòng)態(tài)添加到當(dāng)前路由中;2.將數(shù)據(jù)保存到store中,然后各頁(yè)面根據(jù)store中的數(shù)據(jù)來(lái)渲染菜單。
核心思路并不難,下面我們來(lái)看看實(shí)現(xiàn)步驟。
數(shù)據(jù)請(qǐng)求時(shí)機(jī)
這個(gè)很重要。
可能會(huì)有小伙伴說(shuō)這有何難,登錄成功之后請(qǐng)求不就可以了嗎?是的,登錄成功之后,請(qǐng)求菜單資源是可以的,請(qǐng)求到之后,我們將之保存在store中,以便下一次使用,但是這樣又會(huì)有另外一個(gè)問(wèn)題,假如用戶登錄成功之后,點(diǎn)擊某一個(gè)子頁(yè)面,進(jìn)入到子頁(yè)面中,然后按了一下F5進(jìn)行刷新,這個(gè)時(shí)候就GG了,因?yàn)镕5刷新之后store中的數(shù)據(jù)就沒(méi)了,而我們又只在登錄成功的時(shí)候請(qǐng)求了一次菜單資源,要解決這個(gè)問(wèn)題,有兩種思路:1.將菜單資源不要保存到store中,而是保存到localStorage中,這樣即使F5刷新之后數(shù)據(jù)還在;2.直接在每一個(gè)頁(yè)面的mounted方法中,都去加載一次菜單資源。
由于菜單資源是非常敏感的,因此最好不要不要將其保存到本地,故舍棄方案1,但是方案2的工作量有點(diǎn)大,因此我采取辦法將之簡(jiǎn)化,采取的辦法就是使用路由中的導(dǎo)航守衛(wèi)。
路由導(dǎo)航守衛(wèi)
我的具體實(shí)現(xiàn)是這樣的,首先在store中創(chuàng)建一個(gè)routes數(shù)組,這是一個(gè)空數(shù)組,然后開(kāi)啟路由全局守衛(wèi),如下:
router.beforeEach((to, from, next)=> {
if (to.name == 'Login') {
next();
return;
}
var name = store.state.user.name;
if (name == '未登錄') {
if (to.meta.requireAuth || to.name == null) {
next({path: '/', query: {redirect: to.path}})
} else {
next();
}
} else {
initMenu(router, store);
next();
}
}
)
這里的代碼很短,我來(lái)做一個(gè)簡(jiǎn)單的解釋:
1.如果要去的頁(yè)面是登錄頁(yè)面,這個(gè)沒(méi)啥好說(shuō)的,直接過(guò)。
2.如果不是登錄頁(yè)面的話,我先從store中獲取當(dāng)前的登錄狀態(tài),如果未登錄,則通過(guò)路由中meta屬性的requireAuth屬性判斷要去的頁(yè)面是否需要登錄,如果需要登錄,則跳回登錄頁(yè)面,同時(shí)將要去的頁(yè)面的path作為參數(shù)傳給登錄頁(yè)面,以便在登錄成功之后跳轉(zhuǎn)到目標(biāo)頁(yè)面,如果不需要登錄,則直接過(guò)(事實(shí)上,本項(xiàng)目中只有Login頁(yè)面不需要登錄);如果已經(jīng)登錄了,則先初始化菜單,再跳轉(zhuǎn)。
初始化菜單的操作如下:
export const initMenu = (router, store)=> {
if (store.state.routes.length > 0) {
return;
}
getRequest("/config/sysmenu").then(resp=> {
if (resp && resp.status == 200) {
var fmtRoutes = formatRoutes(resp.data);
router.addRoutes(fmtRoutes);
store.commit('initMenu', fmtRoutes);
}
})
}
export const formatRoutes = (routes)=> {
let fmRoutes = [];
routes.forEach(router=> {
let {
path,
component,
name,
meta,
iconCls,
children
} = router;
if (children && children instanceof Array) {
children = formatRoutes(children);
}
let fmRouter = {
path: path,
component(resolve){
if (component.startsWith("Home")) {
require(['../components/' + component + '.vue'], resolve)
} else if (component.startsWith("Emp")) {
require(['../components/emp/' + component + '.vue'], resolve)
} else if (component.startsWith("Per")) {
require(['../components/personnel/' + component + '.vue'], resolve)
} else if (component.startsWith("Sal")) {
require(['../components/salary/' + component + '.vue'], resolve)
} else if (component.startsWith("Sta")) {
require(['../components/statistics/' + component + '.vue'], resolve)
} else if (component.startsWith("Sys")) {
require(['../components/system/' + component + '.vue'], resolve)
}
},
name: name,
iconCls: iconCls,
meta: meta,
children: children
};
fmRoutes.push(fmRouter);
})
return fmRoutes;
}
在初始化菜單中,首先判斷store中的數(shù)據(jù)是否存在,如果存在,說(shuō)明這次跳轉(zhuǎn)是正常的跳轉(zhuǎn),而不是用戶按F5或者直接在地址欄輸入某個(gè)地址進(jìn)入的。否則就去加載菜單。拿到菜單之后,首先通過(guò)formatRoutes方法將服務(wù)器返回的json轉(zhuǎn)為router需要的格式,這里主要是轉(zhuǎn)component,因?yàn)榉?wù)端返回的component是一個(gè)字符串,而router中需要的卻是一個(gè)組件,因此我們?cè)趂ormatRoutes方法中動(dòng)態(tài)的加載需要的組件即可。數(shù)據(jù)格式準(zhǔn)備成功之后,一方面將數(shù)據(jù)存到store中,另一方面利用路由中的addRoutes方法將之動(dòng)態(tài)添加到路由中。
菜單渲染
最后,在Home頁(yè)中,從store中獲取菜單json,渲染成菜單即可,相關(guān)代碼可以在Home.vue中查看,不贅述。
OK,如此之后,不同用戶登錄成功之后就可以看到不同的菜單了。
相關(guān)文章
詳解vue3+element-plus實(shí)現(xiàn)動(dòng)態(tài)菜單和動(dòng)態(tài)路由動(dòng)態(tài)按鈕(前后端分離)
本文需要使用axios,路由,pinia,安裝element-plus,并且本文vue3是基于js而非ts的,這些環(huán)境如何搭建不做描述,需要讀者自己完成,感興趣的朋友跟隨小編一起看看吧2023-11-11
el-form的model、prop屬性和表單校驗(yàn)等使用
本文主要介紹了el-form的model、prop屬性和表單校驗(yàn)等使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-08-08
深入探討如何在Vue中使用EventBus實(shí)現(xiàn)組件間的高效通信
在現(xiàn)代前端開(kāi)發(fā)中,Vue.js?作為一種流行的漸進(jìn)式框架,廣泛應(yīng)用于各類?Web?項(xiàng)目的構(gòu)建中,本文將深入探討如何在?Vue?中使用?EventBus,實(shí)現(xiàn)組件間的高效通信,需要的可以了解下2024-11-11
Vue通過(guò)moment插件實(shí)現(xiàn)獲取當(dāng)前月的第一天和最后一天
這篇文章主要介紹了Vue 結(jié)合插件moment 實(shí)現(xiàn)獲取當(dāng)前月的第一天和最后一天,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2023-10-10
el-tree樹(shù)設(shè)置懶加載以及設(shè)置默認(rèn)勾選方式
這篇文章主要介紹了el-tree樹(shù)設(shè)置懶加載以及設(shè)置默認(rèn)勾選方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04
vue2+tracking實(shí)現(xiàn)PC端的人臉識(shí)別示例
本文主要介紹了vue2+tracking實(shí)現(xiàn)PC端的人臉識(shí)別示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
vue中使用heatmapjs的示例代碼(結(jié)合百度地圖)
這篇文章主要介紹了vue中使用heatmapjs的示例代碼(結(jié)合百度地圖),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-09-09
Vue中Class和Style實(shí)現(xiàn)v-bind綁定的幾種用法
項(xiàng)目開(kāi)發(fā)中給元素添加/刪除 class 是非常常見(jiàn)的行為之一, 例如網(wǎng)站導(dǎo)航都會(huì)給選中項(xiàng)添加一個(gè) active 類用來(lái)區(qū)別選與未選中的樣式,那么在 vue 中 我們?nèi)绾翁幚磉@類的效果呢?下面我們就一起來(lái)了解一下2021-05-05
vscode 插件開(kāi)發(fā) + vue的操作方法
這篇文章主要介紹了vscode 插件開(kāi)發(fā) + vue的操作方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-06-06

