溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

動(dòng)態(tài)加載權(quán)限管理模塊中的Vue組件

發(fā)布時(shí)間:2020-09-16 02:51:07 來源:腳本之家 閱讀:132 作者:laozhang 欄目:web開發(fā)

本文我們主要來聊聊登錄以及組件的動(dòng)態(tài)加載。

登錄狀態(tài)保存

當(dāng)用戶登錄成功之后,需要將當(dāng)前用戶的登錄信息保存在本地,方便后面使用。具體實(shí)現(xiàn)如下:

登錄成功保存數(shù)據(jù)

在登錄操作執(zhí)行成功之后,通過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主頁之前,向服務(wù)端發(fā)送請求,要求獲取當(dāng)前的菜單信息和組件信息,服務(wù)端根據(jù)當(dāng)前用戶所具備的角色,以及角色所對應(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": "高級資料",
        "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中,然后各頁面根據(jù)store中的數(shù)據(jù)來渲染菜單。

核心思路并不難,下面我們來看看實(shí)現(xiàn)步驟。

數(shù)據(jù)請求時(shí)機(jī)

這個(gè)很重要。

可能會(huì)有小伙伴說這有何難,登錄成功之后請求不就可以了嗎?是的,登錄成功之后,請求菜單資源是可以的,請求到之后,我們將之保存在store中,以便下一次使用,但是這樣又會(huì)有另外一個(gè)問題,假如用戶登錄成功之后,點(diǎn)擊某一個(gè)子頁面,進(jìn)入到子頁面中,然后按了一下F5進(jìn)行刷新,這個(gè)時(shí)候就GG了,因?yàn)镕5刷新之后store中的數(shù)據(jù)就沒了,而我們又只在登錄成功的時(shí)候請求了一次菜單資源,要解決這個(gè)問題,有兩種思路:1.將菜單資源不要保存到store中,而是保存到localStorage中,這樣即使F5刷新之后數(shù)據(jù)還在;2.直接在每一個(gè)頁面的mounted方法中,都去加載一次菜單資源。

由于菜單資源是非常敏感的,因此最好不要不要將其保存到本地,故舍棄方案1,但是方案2的工作量有點(diǎn)大,因此我采取辦法將之簡化,采取的辦法就是使用路由中的導(dǎo)航守衛(wèi)。

路由導(dǎo)航守衛(wèi)

我的具體實(shí)現(xiàn)是這樣的,首先在store中創(chuàng)建一個(gè)routes數(shù)組,這是一個(gè)空數(shù)組,然后開啟路由全局守衛(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();
  }
 }
)

這里的代碼很短,我來做一個(gè)簡單的解釋:

1.如果要去的頁面是登錄頁面,這個(gè)沒啥好說的,直接過。

2.如果不是登錄頁面的話,我先從store中獲取當(dāng)前的登錄狀態(tài),如果未登錄,則通過路由中meta屬性的requireAuth屬性判斷要去的頁面是否需要登錄,如果需要登錄,則跳回登錄頁面,同時(shí)將要去的頁面的path作為參數(shù)傳給登錄頁面,以便在登錄成功之后跳轉(zhuǎn)到目標(biāo)頁面,如果不需要登錄,則直接過(事實(shí)上,本項(xiàng)目中只有Login頁面不需要登錄);如果已經(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ù)是否存在,如果存在,說明這次跳轉(zhuǎn)是正常的跳轉(zhuǎn),而不是用戶按F5或者直接在地址欄輸入某個(gè)地址進(jìn)入的。否則就去加載菜單。拿到菜單之后,首先通過formatRoutes方法將服務(wù)器返回的json轉(zhuǎn)為router需要的格式,這里主要是轉(zhuǎn)component,因?yàn)榉?wù)端返回的component是一個(gè)字符串,而router中需要的卻是一個(gè)組件,因此我們在formatRoutes方法中動(dòng)態(tài)的加載需要的組件即可。數(shù)據(jù)格式準(zhǔn)備成功之后,一方面將數(shù)據(jù)存到store中,另一方面利用路由中的addRoutes方法將之動(dòng)態(tài)添加到路由中。

菜單渲染

最后,在Home頁中,從store中獲取菜單json,渲染成菜單即可,相關(guān)代碼可以在Home.vue中查看,不贅述。

OK,如此之后,不同用戶登錄成功之后就可以看到不同的菜單了。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI