0%

动态路由(前端后端控制)

动态路由

后台管理系统,大部分都会涉及到权限控制这一项需求,即:根据不同登录角色渲染不同页面功能

现在主流有两种方式:

  1. 前端控制

    逻辑简单,上手快

  2. 后端控制

    相对安全,需要后期改动

后端控制

后端路由是大部分后台管理项目的解决方案

核心:用户登录以后,后端根据该角色生成可访问的路由数据,前端根据这个路由数据转换成自己需要的路由结构

具体代码结构:

  1. router 文件中,只放一些静态路由和公共路由

    代码参考:https://github.com/PanJiaChen/vue-element-admin/blob/master/src/router/index.js

  2. 在 vuex 中写一个 state,把路由和获取到的角色进行匹配,控制菜单栏的显示隐藏

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    import { constantRoutes } from '@/router'
    import { getRoutes } from '@/api/menu'
    import Layout from '@/layout/index'

    // 遍历后台传来的路由字符串,转换为组件对象
    function filterAsyncRoutes(asyncRouterMap) {
    return asyncRouterMap.filter(route => {
    if (route.component) {
    // Layout组件特殊处理
    if (route.component === 'Layout') {
    route.component = Layout
    } else {
    route.component = loadView(route.component)
    }
    }
    if (route.children && route.children.length) {
    route.children = filterAsyncRoutes(route.children)
    }
    return true
    })
    }
    function loadView(view) {
    // 注意:webpack4动态import不支持变量方式,如下写法是不行的
    // return () => import(`@/views/${view}`)
    return resolve => require([`@/views/${view}`], resolve)
    }

    const permission = {
    state: {
    routes: [],
    addRoutes: [],
    },
    mutations: {
    SET_ROUTES: (state, routes) => {
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
    },
    },
    actions: {
    GenerateRoutes({ commit }) {
    const Id = sessionStorage.getItem('SESSION_KEY')
    return new Promise(resolve => {
    getRoutes(Id).then(res => {
    const accessedRoutes = filterAsyncRoutes(res.data)
    accessedRoutes.push({ path: '*', redirect: '/404', hidden: true })
    commit('SET_ROUTES', accessedRoutes)
    resolve(accessedRoutes)
    })
    })
    },
    },
    }
  3. 给后端整理一份前端需要的 router 数据结构

    一般必须有的参数:id、path、name、title、children

    1
    2
    3
    4
    5
    6
    7
    8
    9
    path: # 路由地址
    name: # 路由名称
    id: # id
    component: # 组件路径
    meta->title: # 菜单名称(和path同级就可以)
    meta->icon: # 菜单图标(和path同级就可以)
    meta->type: # 菜单类型,用于区分模块、目录、菜单、按钮
    meta->hidden: # 是否全局隐藏此菜单
    children: # 子集集合

    如果后端传的不是 children,是 parentId 那种类型,则需要写一个转换方法

    方法可以参考这篇文章:Vue 封装无限层级树形菜单组件(后台传的是扁平数组)

  4. 在导航守卫中,使用 router.beforeEach 进行拦截,可以动态添加可访问的路由表(使用 addRoutes 添加)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    import NProgress from 'nprogress'
    import 'nprogress/nprogress.css'

    NProgress.configure({ showSpinner: false })
    const whiteList = ['/login', '/auth-redirect', '/bind', '/register']

    router.beforeEach((to, from, next) => {
    NProgress.start()
    // 判断是否有token
    if (store.getters.token) {
    if (to.path === '/login') {
    next({ path: '/' })
    } else {
    // 判断当前用户是否已拉取完user_info信息
    if (store.getters.roles.length === 0) {
    // 获取info信息
    store.dispatch('GetInfo').then(res => {
    const roles = res.roles
    store
    .dispatch('GenerateRoutes', { roles })
    .then(accessRoutes => {
    // 动态添加可访问路由表
    router.addRoutes(accessRoutes)
    // hack方法 确保addRoutes已完成
    next({ ...to, replace: true })
    })
    .catch(err => {
    console.log(err)
    })
    })
    } else {
    // 当有用户权限的时候,说明可访问路由表已生成
    next()
    }
    }
    } else {
    if (whiteList.includes(to.path)) {
    // 在免登录白名单,直接进入
    next()
    } else {
    // 否则全部重定向到登录页
    next('/login')
    }
    }
    })
  5. 从 router 中取出可用的路由对象,来进行侧边栏的渲染

也可以参考这篇文章:后端控制路由

前端控制

可以参考花裤衩的文章,手摸手,带你用vue撸后台 系列二(登录权限篇)

核心:通过 token 获取用户的 role,根据 role 动态跟路由表 meta.role 进行匹配,形成可访问的路由再通过 router.addRotes 动态挂载路由

具体代码结构:

  1. 把动态路由和静态路由分别写在 router 文件中(asyncRoutes/constantRoutes)。在动态路由的 meta 元信息中添加 roles 权限

    代码参考:https://github.com/PanJiaChen/vue-element-admin/blob/master/src/router/index.js

  2. 在 vuex 中写一个 state,把路由和获取到的角色进行匹配,控制菜单栏的显示隐藏

    代码参考:https://github.com/PanJiaChen/vue-element-admin/blob/master/src/store/modules/permission.js

  3. 在导航守卫中,使用 router.beforeEach 进行拦截,可以动态添加可访问的路由表(使用 addRoutes 添加)

    https://github.com/PanJiaChen/vue-element-admin/blob/master/src/permission.js

  4. 从 router 中取出可用的路由对象,来进行侧边栏的渲染

    https://github.com/PanJiaChen/vue-element-admin/blob/master/src/layout/components/Sidebar/index.vue

    https://github.com/PanJiaChen/vue-element-admin/blob/master/src/layout/components/Sidebar/SidebarItem.vue