這次帶給大家怎麼使用vue,vue-router, vuex及addRoutes進行權限控制,使用vue,vue-router, vuex及addRoutes進行權限控制的注意事項有哪些,下面就是實戰案例,一起來看一下。
基於vuex, vue-router,vuex的權限控制教學,完整程式碼位址請見https://github.com/linrunzheng/vue- permission-control
接下來讓我們模擬一個一般使用者開啟網站的流程,一步一步走完整的流程。
首先從開啟本機的服務localhost:8080開始,我們知道開啟後會進入login頁面,那麼判斷的依據是什麼。
首先是token。
沒有登陸的使用者是取得不到token的,而登陸後的角色我們會將token存到local或seesionStorage 因此,根據目前有沒有token即可知道是否登陸。
為了存取token並且方便我們操作,可以配和vuex實作
/* state.js */ export default { get UserToken() { return localStorage.getItem('token') }, set UserToken(value) { localStorage.setItem('token', value) } } /* mutation.js */ export default { LOGIN_IN(state, token) { state.UserToken = token }, LOGIN_OUT(state) { state.UserToken = '' } }
攔截的判斷
沒有token進入需要權限的頁面:redirect到login頁面
由於我們路由是動態掛載的,包括' ' 和404,所以當匹配不到路由時,也重定向到login
router.beforeEach((to, from, next) => { if (!store.state.UserToken) { if ( to.matched.length > 0 && !to.matched.some(record => record.meta.requiresAuth) ) { next() } else { next({ path: '/login' }) } } })
好了,此時使用者開啟localhost:8080,預設符合的是''路徑,此時我們並沒有掛載路由,也沒有token,所以來到了login。
輸入使用者名稱密碼後,有token了,透過store觸發* commit('LOGIN_IN')* 來設定token。
但還是沒有路由,目前最開始只有login路由
/* 初始路由 */ export default new Router({ routes: [ { path: '/login', component: Login } ] }) /* 准备动态添加的路由 */ export const DynamicRoutes = [ { path: '', component: Layout, name: 'container', redirect: 'home', meta: { requiresAuth: true, name: '首页' }, children: [ { path: 'home', component: Home, name: 'home', meta: { name: '首页' } } ] }, { path: '/403', component: Forbidden }, { path: '*', component: NotFound } ]
我們要根據目前使用者的token去後台取得權限。
由於權限這塊邏輯還挺多,所以在vuex中加入了一個permission模組來處理權限。
為了判斷是已有路由列表,需要在vuex的permission模組存一個state狀態permissionList用來判斷,假如permissionList不為null,即已經有路由,如果不存在,就需要我們幹活了。
router.beforeEach((to, from, next) => { if (!store.state.UserToken) { ... } else { /* 现在有token了 */ if (!store.state.permission.permissionList) { /* 如果没有permissionList,真正的工作开始了 */ store.dispatch('permission/FETCH_PERMISSION').then(() => { next({ path: to.path }) }) } else { if (to.path !== '/login') { next() } else { next(from.fullPath) } } } })
來看store.dispatch('permission/FETCH_PERMISSION') 都做了什麼
actions: { async FETCH_PERMISSION({ commit, state }) { /* 获取后台给的权限数组 */ let permissionList = await fetchPermission() /* 根据后台权限跟我们定义好的权限对比,筛选出对应的路由并加入到path=''的children */ let routes = recursionRouter(permissionList, dynamicRouter) let MainContainer = DynamicRoutes.find(v => v.path === '') let children = MainContainer.children children.push(...routes) /* 生成左侧导航菜单 */ commit('SET_MENU', children) setDefaultRoute([MainContainer]) /* 初始路由 */ let initialRoutes = router.options.routes /* 动态添加路由 */ router.addRoutes(DynamicRoutes) /* 完整的路由表 */ commit('SET_PERMISSION', [...initialRoutes, ...DynamicRoutes]) } }
首先,await fetchPermission()取得後台給的權限數組,格式大概如下
{ "code": 0, "message": "获取权限成功", "data": [ { "name": "订单管理", "children": [ { "name": "订单列表" }, { "name": "生产管理", "children": [ { "name": "生产列表" } ] }, { "name": "退货管理" } ] } ] }
其次根據我們寫好的路由數組,進行對比,過濾得到我們要的路由
/* 这里是我们写好的需要权限判断的路由 */ const dynamicRoutes = [ { path: '/order', component: Order, name: 'order-manage', meta: { name: '订单管理' }, children: [ { path: 'list', name: 'order-list', component: OrderList, meta: { name: '订单列表' } }, { path: 'product', name: 'product-manage', component: ProductManage, meta: { name: '生产管理' }, children: [ { path: 'list', name: 'product-list', component: ProductionList, meta: { name: '生产列表' } }, { path: 'review', name: 'review-manage', component: ReviewManage, meta: { name: '审核管理' } } ] }, { path: 'returnGoods', name: 'return-goods', component: ReturnGoods, meta: { name: '退货管理' } } ] } ] export default dynamicRoutes
為了對比,我寫好了一個遞歸函數,用name和meta .name進行對比,根據這個函數就可以得到我們想要的結果
/** * * @param {Array} userRouter 后台返回的用户权限json * @param {Array} allRouter 前端配置好的所有动态路由的集合 * @return {Array} realRoutes 过滤后的路由 */ export function recursionRouter(userRouter = [], allRouter = []) { var realRoutes = [] allRouter.forEach((v, i) => { userRouter.forEach((item, index) => { if (item.name === v.meta.name) { if (item.children && item.children.length > 0) { v.children = recursionRouter(item.children, v.children) } realRoutes.push(v) } }) }) return realRoutes }
得到過濾後的數組後,加入到path為''的children下面
{ path: '', component: Layout, name: 'container', redirect: 'home', meta: { requiresAuth: true, name: '首页' }, children: [ { path: 'home', component: Home, name: 'home', meta: { name: '首页' } }, <!-- 将上面得到的东西加入到这里 --> ... ] }
這個時候,path為''的children就是我們左側的導覽選單了,存到state的sidebarMenu待用。加入children後,這時DynamicRoutes就可以加入到路由了。
/* 动态添加路由 */ router.addRoutes(DynamicRoutes) /* 初始路由 */ let initialRoutes = router.options.routes /* 合并起来,就是完整的路由了 */ commit('SET_PERMISSION', [...initialRoutes, ...DynamicRoutes])
路由加完了,也就是action操作完畢了,即可在action.then裡面呼叫next({ path: to.path })進去路由,這裡要注意, next裡面要傳參數即要進入的頁面的路由訊息,因為next傳參數後,目前要進入的路由會被廢止,轉而進入參數對應的路由,雖然是同一個路由,這麼做主要是為了確保addRoutes生效了。
進入路由後,要開始產生左側選單,之前我們已經存到sidebarMenu了,現在需要做的只是遞歸生成選單而已,雖然用了element的導航選單,但是為了遞歸路由,還需要自己封裝一下。這裡核心的地方是元件的name,在元件裡面有children的地方,又再次使用自己,從而遍歷整個tree結構的路由。
<template> <p class="menu-container"> <template v-for="v in menuList"> <el-submenu :index="v.name" v-if="v.children&&v.children.length>0" :key="v.name"> <template slot="title"> <i class="iconfont icon-home"></i> <span>{{v.meta.name}}</span> </template> <el-menu-item-group> <my-nav :menuList="v.children"></my-nav> </el-menu-item-group> </el-submenu> <el-menu-item :key="v.name" :index="v.name" @click="gotoRoute(v.name)" v-else> <i class="iconfont icon-home"></i> <span slot="title">{{v.meta.name}}</span> </el-menu-item> </template> </p> </template> <script> export default { name: 'my-nav', props: { menuList: { type: Array, default: function() { return [] } } }, methods: { gotoRoute(name) { this.$router.push({ name }) } } } </script>
刷新頁面後,根據我們router.beforeEach的判斷,有token但是沒permissionList,我們是會重新觸發action去獲取路由的,所以無需擔心。但是導航選單active效果會不見。不過我們已經把el-menu-item的key設定為路由的name,那麼我們只要在刷新後,在afterEach把目前路由的name賦值給el-menu default-active即可。同理,在afterEach階段取得所有matched的路由,即可實現麵包屑導航。
if (!store.state.permission.permissionList) { store.dispatch('permission/FETCH_PERMISSION').then(() => { next({ path: to.path }) }) } ... router.afterEach((to, from, next) => { var routerList = to.matched store.commit('setCrumbList', routerList) store.commit('permission/SET_CURRENT_MENU', to.name) })
退出登陆后,需要刷新页面,因为我们是通过addRoutes添加的,router没有deleteRoutes这个api,所以清除token,清除permissionList等信息,刷新页面是最保险的。
最后还有一点,每次请求得带上token, 可以对axios封装一下来处理
var instance = axios.create({ timeout: 30000, baseURL }) // 添加请求拦截器 instance.interceptors.request.use( function(config) { // 请求头添加token if (store.state.UserToken) { config.headers.Authorization = store.state.UserToken } return config }, function(error) { return Promise.reject(error) } ) /* axios请求二次封装 */ instance.get = function(url, data, options) { return new Promise((resolve, reject) => { axios .get(url, data, options) .then( res => { var response = res.data if (response.code === 0) { resolve(response.data) } else { Message.warning(response.message) /* reject(response.message) */ } }, error => { if (error.response.status === 401) { Message.warning({ message: '登陆超时,请重新登录' }) store.commit('LOGIN_OUT') window.location.reload() } else { Message.error({ message: '系统异常' }) } reject(error) } ) .catch(e => { console.log(e) }) }) } export default instance
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上是怎麼使用vue,vue-router, vuex及addRoutes進行權限控制的詳細內容。更多資訊請關注PHP中文網其他相關文章!