首頁  >  文章  >  web前端  >  權限管理模組中動態載入Vue組件實例詳解

權限管理模組中動態載入Vue組件實例詳解

小云云
小云云原創
2018-01-16 17:09:312002瀏覽

本系列文章並不是手把手的教程,主要介紹了核心思路並講解了核心代碼,完整的代碼小伙伴們可以在GitHub上star並clone下來研究。另外,原本打算把項目跑起來放到網上供小伙伴們查看,但是之前買服務器為了省錢,內存只有512M,兩個應用跑不起來(已經有一個V部落開源項目在運行),因此小伙伴們只能將就看一下下面的截圖了,GitHub上有部署教程,部署到本地也可以查看完整效果。


專案網址:https://github.com/lenve/vhr

前面幾篇文章,我們基本上已經解決了服務端的問題,並封裝了前端請求,本文我們主要來聊聊登入以及元件的動態載入。

本文是本系列的第五篇,建議先閱讀前面的文章有助於更好的理解本文:

1. SpringBoot+Vue前後端分離,使用SpringSecurity完美處理權限問題(一)  
2.SpringBoot+Vue前後端分離,使用SpringSecurity完美處理權限問題(二)  
3.SpringSecurity中密碼加鹽與SpringBoot中異常統一處理 
4.axios請求封裝與異常統一處理

#登入狀態保存

當使用者登入成功之後,需要將目前使用者的登入資訊保存在本地,方便後面使用。具體實現如下:

登入成功保存資料

#在登入作業執行成功之後,透過commit作業將資料提交到store中,核心程式碼如下:

<span style="font-size: 14px;">this.postRequest('/login', {<br>    username: this.loginForm.username,<br>    password: this.loginForm.password<br>}).then(resp=> {<br>    if (resp && resp.status == 200) {<br>    var data = resp.data;<br>    _this.$store.commit('login', data.msg);<br>    var path = _this.$route.query.redirect;<br>    _this.$router.replace({path: path == '/' || path == undefined ? '/home' : path});<br>    }<br>});<br></span>

store

#store的核心程式碼如下:

<span style="font-size: 14px;">export default new Vuex.Store({<br>  state: {<br>    user: {<br>      name: window.localStorage.getItem('user' || '[]') == null ? '未登录' : JSON.parse(window.localStorage.getItem('user' || '[]')).name,<br>      userface: window.localStorage.getItem('user' || '[]') == null ? '' : JSON.parse(window.localStorage.getItem('user' || '[]')).userface<br>    }<br>  },<br>  mutations: {<br>    login(state, user){<br>      state.user = user;<br>      window.localStorage.setItem('user', JSON.stringify(user));<br>    },<br>    logout(state){<br>      window.localStorage.removeItem('user');<br>    }<br>  }<br>});<br></span>

為了減少麻煩,使用者登入成功後的資料將被保存在localStorage中(防止使用者按F5刷新之後資料遺失),以字串的形式存入,取的時候再轉為json。當使用者登出登陸時,將localStorage中的資料清除。

元件動態載入

在權限管理模組中,這算是前端的核心了。

核心思路

用戶在登入成功之後,進入home主頁之前,向服務端發送請求,要求取得當前的選單資訊和元件訊息,服務端根據目前使用者所具備的角色,以及角色所對應的資源,傳回一個json字串,格式如下:

<span style="font-size: 14px;">[<br>    {<br>        "id": 2,<br>        "path": "/home",<br>        "component": "Home",<br>        "name": "员工资料",<br>        "iconCls": "fa fa-user-circle-o",<br>        "children": [<br>            {<br>                "id": null,<br>                "path": "/emp/basic",<br>                "component": "EmpBasic",<br>                "name": "基本资料",<br>                "iconCls": null,<br>                "children": [],<br>                "meta": {<br>                    "keepAlive": false,<br>                    "requireAuth": true<br>                }<br>            },<br>            {<br>                "id": null,<br>                "path": "/emp/adv",<br>                "component": "EmpAdv",<br>                "name": "高级资料",<br>                "iconCls": null,<br>                "children": [],<br>                "meta": {<br>                    "keepAlive": false,<br>                    "requireAuth": true<br>                }<br>            }<br>        ],<br>        "meta": {<br>            "keepAlive": false,<br>            "requireAuth": true<br>        }<br>    }<br>]<br></span>

前端在拿到這個字串之後,做兩件事:1.將json動態加入到目前路由;2.將資料儲存到store中,然後各頁根據store中的資料來渲染選單。

核心想法並不難,下面我們來看看實作步驟。

資料請求時機

這很重要。

可能會有小夥伴說這有何難,登入成功之後請求不就可以了嗎?是的,登入成功之後,請求選單資源是可以的,請求到之後,我們將之保存在store中,以便下一次使用,但是這樣又會有另外一個問題,假如用戶登入成功之後,點擊某一個子頁面,進入子頁面中,然後按了一下F5進行刷新,這個時候就GG了,因為F5刷新之後store中的資料就沒了,而我們又只在登入成功的時候請求了一次選單資源,要要解決這個問題,有兩種想法:1.將選單資源不要儲存到store中,而是儲存到localStorage中,這樣即使F5刷新之後資料還在;2.直接在每一個頁面的mounted方法中,都去載入一次選單資源。

由於選單資源是非常敏感的,因此最好不要將其保存到本地,故捨棄方案1,但是方案2的工作量有點大,因此我採取辦法將之簡化,採取的辦法就是使用路由中的導航守衛。

路由導航守衛

我的具體實作是這樣的,首先在store中建立一個routes數組,這是一個空數組,然後開啟路由全域守衛,如下:

<span style="font-size: 14px;">router.beforeEach((to, from, next)=> {<br>    if (to.name == 'Login') {<br>      next();<br>      return;<br>    }<br>    var name = store.state.user.name;<br>    if (name == '未登录') {<br>      if (to.meta.requireAuth || to.name == null) {<br>        next({path: '/', query: {redirect: to.path}})<br>      } else {<br>        next();<br>      }<br>    } else {<br>      initMenu(router, store);<br>      next();<br>    }<br>  }<br>)<br></span>

這裡的程式碼很短,我來做一個簡單的解釋:  
1.如果要去的頁面是登入頁面,這個沒啥好說的,直接過。

2.如果不是登录页面的话,我先从store中获取当前的登录状态,如果未登录,则通过路由中meta属性的requireAuth属性判断要去的页面是否需要登录,如果需要登录,则跳回登录页面,同时将要去的页面的path作为参数传给登录页面,以便在登录成功之后跳转到目标页面,如果不需要登录,则直接过(事实上,本项目中只有Login页面不需要登录);如果已经登录了,则先初始化菜单,再跳转。

初始化菜单的操作如下:

<span style="font-size: 14px;">export const initMenu = (router, store)=> {<br>  if (store.state.routes.length > 0) {<br>    return;<br>  }<br>  getRequest("/config/sysmenu").then(resp=> {<br>    if (resp && resp.status == 200) {<br>      var fmtRoutes = formatRoutes(resp.data);<br>      router.addRoutes(fmtRoutes);<br>      store.commit('initMenu', fmtRoutes);<br>    }<br>  })<br>}<br>export const formatRoutes = (routes)=> {<br>  let fmRoutes = [];<br>  routes.forEach(router=> {<br>    let {<br>      path,<br>      component,<br>      name,<br>      meta,<br>      iconCls,<br>      children<br>    } = router;<br>    if (children && children instanceof Array) {<br>      children = formatRoutes(children);<br>    }<br>    let fmRouter = {<br>      path: path,<br>      component(resolve){<br>        if (component.startsWith("Home")) {<br>          require(['../components/' + component + '.vue'], resolve)<br>        } else if (component.startsWith("Emp")) {<br>          require(['../components/emp/' + component + '.vue'], resolve)<br>        } else if (component.startsWith("Per")) {<br>          require(['../components/personnel/' + component + '.vue'], resolve)<br>        } else if (component.startsWith("Sal")) {<br>          require(['../components/salary/' + component + '.vue'], resolve)<br>        } else if (component.startsWith("Sta")) {<br>          require(['../components/statistics/' + component + '.vue'], resolve)<br>        } else if (component.startsWith("Sys")) {<br>          require(['../components/system/' + component + '.vue'], resolve)<br>        }<br>      },<br>      name: name,<br>      iconCls: iconCls,<br>      meta: meta,<br>      children: children<br>    };<br>    fmRoutes.push(fmRouter);<br>  })<br>  return fmRoutes;<br>}<br></span>

在初始化菜单中,首先判断store中的数据是否存在,如果存在,说明这次跳转是正常的跳转,而不是用户按F5或者直接在地址栏输入某个地址进入的。否则就去加载菜单。拿到菜单之后,首先通过formatRoutes方法将服务器返回的json转为router需要的格式,这里主要是转component,因为服务端返回的component是一个字符串,而router中需要的却是一个组件,因此我们在formatRoutes方法中动态的加载需要的组件即可。数据格式准备成功之后,一方面将数据存到store中,另一方面利用路由中的addRoutes方法将之动态添加到路由中。

菜单渲染

最后,在Home页中,从store中获取菜单json,渲染成菜单即可,相关代码可以在<span style="font-size: 14px;">Home.vue</span>中查看,不赘述。

OK,如此之后,不同用户登录成功之后就可以看到不同的菜单了。

相关推荐:

vue组件之Alert详解

jquery加载单文件vue组件方法分享

实例详解vue组件父子间通信之聊天室


以上是權限管理模組中動態載入Vue組件實例詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn