이번에는 vue+vue-router+vuex 작업 권한에 대해 알려드리겠습니다. vue+vue-router+vuex 작업 권한에 대한 주의 사항은 무엇인가요?
vuex, vue-router, vuex를 기반으로 한 권한 제어 튜토리얼, 전체 코드 주소는 https://github.com/linrunzheng/vue-permission-control
에서 확인할 수 있습니다.다음, 시뮬레이션해 보겠습니다. 일반 사용자가 웹 사이트를 열려면 전체 프로세스를 단계별로 진행하세요.
먼저 로컬 서비스 localhost:8080을 열어서 시작합니다. 오픈 후 로그인 페이지로 들어갈 것이라는 것을 알고 있는데 판단의 근거는 무엇인가요?
먼저 토큰입니다.
로그인하지 않은 사용자는 토큰을 얻을 수 없습니다. 로그인한 캐릭터의 경우 로컬 또는 seesionStorage에 토큰이 저장되므로, 로그인 여부에 따라 알 수 있습니다. 현재 토큰.
토큰에 액세스하고 작업을 용이하게 하기 위해 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 = '' } }
차단 판단
권한이 필요한 페이지에 들어갈 토큰이 없는 경우: 로그인으로 리디렉션 page
우리의 경로는 '' 및 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을 열고 기본 일치는 '' 경로인데, 현재는 해당 경로를 마운트하지 않았고, 토큰도 없어 로그인을 하게 되었습니다.
사용자 이름과 비밀번호를 입력한 후 토큰을 설정하기 위해 스토어를 통해 *commit('LOGIN_IN')*을 실행합니다.
하지만 아직 경로가 없습니다. 현재는 로그인 경로만 있습니다
/* 初始路由 */ 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 } ]
현재 사용자의 토큰을 기반으로 권한을 얻으려면 백그라운드로 이동해야 합니다.
권한에는 꽤 많은 로직이 있으므로, 권한을 처리하기 위해 vuex에 권한 모듈이 추가되었습니다.
기존 경로 목록이 있는지 확인하려면 vuex의 권한 모듈에 상태 허가 목록을 저장해야 합니다. 허가 목록이 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]) } }
먼저, wait 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
비교를 위해 재귀 함수를 작성하고 이 함수를 기반으로 이름 및 메타.이름과 비교합니다. want
/** * * @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 }
get 배열을 필터링한 후 ''
{ path: '', component: Layout, name: 'container', redirect: 'home', meta: { requiresAuth: true, name: '首页' }, children: [ { path: 'home', component: Home, name: 'home', meta: { name: '首页' } }, <!-- 将上面得到的东西加入到这里 --> ... ] }
경로가 있는 하위 항목에 추가합니다. 이때 '' 경로가 있는 하위 항목은 왼쪽 탐색 메뉴이며 나중에 사용할 수 있도록 상태의 사이드바 메뉴에 저장됩니다. 하위 항목에 추가한 후 DynamicRoutes를 경로에 추가할 수 있습니다.
/* 动态添加路由 */ router.addRoutes(DynamicRoutes) /* 初始路由 */ let initialRoutes = router.options.routes /* 合并起来,就是完整的路由了 */ commit('SET_PERMISSION', [...initialRoutes, ...DynamicRoutes])
경로가 추가된 후, 즉 액션 작업이 완료된 후 action.then에서 next({ path: to.path })를 호출하여 경로를 입력할 수 있습니다. 여기서는 next에 매개변수가 전달된다는 점에 유의하세요. , 입력되는 페이지입니다. 다음 매개변수를 전달하면 현재 입력 중인 경로가 취소되고 해당 매개변수에 해당하는 경로가 입력되기 때문입니다. addRoutes가 적용되는지 확인합니다.
라우팅을 입력한 후 왼쪽 메뉴 생성을 시작해야 합니다. 이전에 이미 사이드바 메뉴에 저장해 두었습니다. 이제 재귀적 라우팅을 위해 요소의 탐색 메뉴를 사용하기만 하면 됩니다. , 우리는 그것을 스스로 캡슐화해야 합니다. 여기서 핵심은 구성 요소의 이름입니다. 구성 요소에 하위 항목이 있으면 전체 트리 구조의 라우팅을 다시 사용합니다.
<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>
라우터에 따르면 페이지를 새로 고친 후 각 판단 전에 토큰은 있지만 허가 목록은 없습니다. 경로를 얻기 위한 작업을 다시 트리거하므로 걱정할 필요가 없습니다. 하지만 내비게이션 메뉴 활성 효과는 사라집니다. 하지만 el-menu-item의 키를 경로 이름으로 설정했기 때문에 새로 고침 후 afterEach에서 현재 경로의 이름을 el-menu default-active에 할당하기만 하면 됩니다. 같은 방식으로 breadcrumb 탐색은 afterEach 단계에서 일치하는 모든 경로를 획득하여 달성할 수 있습니다.
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 작업 권한의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!