当前位置:首页 » 《关注互联网》 » 正文

前端单独实现 vue 动态路由

20 人参与  2024年10月12日 11:20  分类 : 《关注互联网》  评论

点击全文阅读


前端单独实现 vue 动态路由

Vue 动态路由权限是指在 Vue 应用程序中,根据用户的权限动态生成和控制路由的行为。这意味着不是所有的路由都在应用启动时就被硬编码到路由配置中,而是根据用户的权限信息,在运行时动态地决定哪些路由应该被加载和显示。

动态路由的优点:

安全性:

只有经过验证的用户才能访问其权限范围内的页面。减少了由于硬编码路由导致的安全漏洞。

灵活性:

可以根据用户的权限动态调整应用的结构,无需重新部署整个应用即可调整路由。支持按需加载(懒加载),提高应用性能。

用户体验:

只展示用户可以访问的菜单项,避免显示无用链接,提高用户体验。用户界面更加简洁,只显示与其角色相关的功能。

可维护性:

简化了路由配置,因为不需要为每个角色单独编写路由配置,而是集中管理权限。更容易扩展和修改权限配置,只需更新前端的权限数据即可。

开发效率:

开发者只需要关注业务逻辑,而不需要关心每个角色的具体路由配置。减少了重复工作,提高了开发效率。

实现步骤

定义静态路由配置:

在项目中定义一个包含所有可能路由的静态配置文件或对象,每个路由可以附加权限信息(如角色、访问级别等)。

用户登录与鉴权:

用户登录时,前端存储用户的权限信息(如角色、权限列表等)。

动态生成路由:

根据用户的权限信息,从前端的静态路由配置中筛选出用户有权访问的路由。使用递归算法或其他逻辑动态生成路由配置,并添加到 Vue Router 实例中。

动态渲染菜单:

根据动态生成的路由表来渲染左侧菜单或顶部导航栏,确保只显示用户有权访问的菜单项。

代码示例

配置路由器

router/index.js

// router/index.jsimport Vue from 'vue'import VueRouter from 'vue-router'import Layout from '@/Layout/index.vue'Vue.use(VueRouter)// export const roleMap = {//     '-1':'运维管理员',//     '1':'普通用户',//     '2':'项目经理',//     '3':'部门管理员',//     '4':'综合部管理员',//     '5':'部门领导'// }// 公共路由export const routes = [  {    path: '/',    name: 'redirect',    component: Layout,    hidden: true, // 隐藏菜单    redirect: "/homePage", // 用户在地址栏输入 '/' 时会自动重定向到 /homePage 页面  },  {    path: '/homePage',    component: Layout,    redirect: "/homePage/index",    meta: {      title: "首页",    },    children: [      {        path: 'index',        name: 'homePageIndex',        meta: {          title: "首页",        },        component: () => import('@/views/homePage/index.vue')      }    ]  },  {    path: '/login',    component: () => import('@/views/login.vue'),    hidden: true  },  {    path: '/404',    component: () => import('@/views/error/404.vue'),    hidden: true  },  {    path: '/401',    component: () => import('@/views/error/401.vue'),    hidden: true  },]// 动态权限路由export const dynamicRoutes = [  {    path: '/admin',    meta: {      title: "系统管理",    },    component: Layout,    permission: ['-1', '2', '3', '4', '5'], // all 所有角色都可以访问  1 普通用户  2 项目经理  3 部门管理员  4 综合部管理员  5  部门领导  -1 项目运维管理员    children: [      {        path: 'user',        name: 'userIndex',        meta: {          title: "用户管理",        },        permission: ['-1', '2', '3', '4', '5'],        component: () => import('@/views/admin/user/index.vue')      },      {        path: 'role',        name: 'roleIndex',        meta: {          title: "角色管理",        },        permission: ['-1', '2', '3', '4', '5'],        component: () => import('@/views/admin/role/index.vue'),        children: [          {            path: 'add',            name: 'addRole',            meta: {              title: "添加角色",            },            permission: ['-1',, '3', '4', '5'],            component: () => import('@/views/admin/user/index.vue')          },          {            path: 'update',            name: 'updateRole',            meta: {              title: "编辑角色",            },            permission: ['-1', '2', '3', '4', '5'],            component: () => import('@/views/admin/role/index.vue')          }        ]      }    ]  },  {    path: '/tableEcho',    meta: {      title: "表格管理",    },    component: Layout,    permission: ['-1', '1', '2'],    children: [      {        path: 'test',        name: 'tableEchoIndex',        meta: {          title: "表格测试",        },        permission: ['-1', '1', '2'],        component: () => import('@/views/tableEcho/index.vue'),        children: [          {            path: 'add',            name: 'addTable',            hidden: true,            meta: {              title: "新增测试",            },            permission: ['-1', '2'],            component: () => import('@/views/tableEcho/add.vue')          }        ]      },    ],  },]const router = new VueRouter({  base: process.env.BASE_URL,  routes})export default router

上述代码定义了一个公共路由 routes 和一个动态权限控制的路由 dynamicRoutes , permission 数组定义了哪些角色拥有该路由权限, 将用户分为6个角色级别, 每个角色对应不同的角色级别,分别为

‘-1’:‘运维管理员’,‘1’:‘普通用户’,‘2’:‘项目经理’,‘3’:‘部门管理员’,‘4’:‘综合部管理员’,‘5’:‘部门领导’,

封装路由守卫

permission.js

// permission.jsimport router from './router'import store from './store'import { Message } from 'element-ui'import { getStore } from '@/utils/store';const whiteList = ['/login', '/404', '/401'];router.beforeEach((to, from, next) => {  let token = getStore('token');  if (token) {    /* has token*/    if (to.path === '/login') {      next({ path: '/' });    } else {      if (store.getters.roles.length === 0) {        // 判断当前用户是否已拉取完user_info信息        store.dispatch('GetInfo').then((res) => {          console.log('--------------', res);          router.addRoutes(res) // 动态添加可访问路由表          next({ ...to, replace: true }) // hack方法 确保addRoutes已完成        }).catch(err => {          store.dispatch('LogOut').then(() => {            Message.error(err)            next(`/`)          })        })      } else {        next()      }    }  } else {    // 没有token    if (whiteList.indexOf(to.path) !== -1) {      // 在免登录白名单,直接进入      next()    } else {      next(`/login`) // 否则全部重定向到登录页    }  }})

上述代码表示在路由的 beforeEach 函数里面调用 vuex 里面 actions 里的方法发送接口请求获取用户信息与用户角色权限, 最后通过 router.addRoutes(res) 渲染路由
permission.js 文件需引入到 main.js里面

如果项目 vue-router 版本超过 3.3.0, 需要遍历路由数组再使用 router.addRoute() 方法逐个添加路由

res.forEach( route => {router.addRoute(route);})

在 vuex 里获取用户所拥有的权限, 过滤该权限不拥有的路由

store/index.js

// store/index.jsimport Vue from 'vue'import Vuex from 'vuex'import { routes, dynamicRoutes } from "@/router";import { login, getInfo, logout } from "@/api/user";import { setStore, clearStore } from '@/utils/store';Vue.use(Vuex)export default new Vuex.Store({  state: {    routes,    token: "",    roleType: "",    roles: [],    permissions: [],    sidebarRouters: [],  },  getters: {    token: state => state.token,    roles: state => state.roles,    permissions: state => state.permissions,    sidebarRouters: state => state.sidebarRouters,  },  mutations: {    SET_TOKEN: (state, token) => {      state.token = token;    },    SET_USERINFO: (state, user) => {      state.userInfo = user;    },    SET_ROLETYPE: (state, roleType) => {      state.roleType = roleType;    },    SET_ROLES: (state, roles) => {      state.roles = roles;    },    SET_PERMISSIONS: (state, permissions) => {      state.permissions = permissions;    },    SET_ROUTE: (state, sidebarRouters) => {      state.sidebarRouters = sidebarRouters;    },  },  actions: {    Login({ commit }, userInfo) {      return new Promise((resolve, reject) => {        login(userInfo).then(res => {          setToken(res.data.token);          setStore('token', res.data.token);          commit('SET_TOKEN', res.data.token);          resolve();        }).catch(error => {          reject(error);        })      })    },    // 获取用户信息    GetInfo({ commit }) {      return new Promise((resolve, reject) => {        getInfo().then(res => {          console.log('res::: ', res);          if (res.data.code === 0 || 200) {            const user = res.data.sysUser;            const roleType = res.data.roleType;            commit('SET_USERINFO', user);            // roleType 用户所用的权限级别 1 普通用户  2 项目经理  3 部门管理员  4 综合部管理员  5  部门领导  -1 项目运维管理员            setStore('ROLE_TYPE', roleType);            if (res.data.roles) { // 验证返回的roles是否为真              commit('SET_ROLES', res.data.roles);              commit('SET_PERMISSIONS', res.data.permissions);            } else {              commit('SET_ROLES', ['ROLE_DEFAULT']);            }            // 过滤路由            let newRouters = filterRouter(roleType, dynamicRoutes);            // 连接公共路由            const sidebarRouters = routes.concat([...newRouters])            commit('SET_ROUTE', sidebarRouters);            resolve(sidebarRouters);          } else {            reject(error);          }        }).catch(error => {          reject(error);        })      })    },    // 退出系统    LogOut({ commit, state }) {      return new Promise((resolve, reject) => {        logout(state.token).then(() => {          commit('SET_TOKEN', '')          commit('SET_ROLES', [])          commit('SET_PERMISSIONS', [])          clearStore('token');          clearStore('userInfo')          resolve()        }).catch(error => {          reject(error)        })      })    },  },  modules: {  }})function filterRouter(roleType, routes) {  return routes.filter(item => {  // filter 方法创建一个新数组, 其包含通过所提供函数实现的测试的所有元素,测试未通过的元素会自动剔除    // 如果一级路由的 permission 含有当前角色的 roleType    if (item.permission.includes(roleType)) {      // 如果该一级路由含有子路由时,递归调用该函数判断子路由是否有权限      if (Array.isArray(item.children) && item.children.length > 0) {        // 递归调用该函数,最后接受校验通过后的子路由        let newChildren = filterRouter(roleType, item.children);        // 如果子路由有值,则赋值给当前路由的 children,剔除校验不通过的子路由        if (newChildren.length > 0) {          item.children = newChildren;        } else if (newChildren.length === 0) { // 如果子路由为空,则删除该路由的 children 属性          delete item.children;        }      }      // 最后返回 true, 表示该路由通过权限校验      return true;    }  })}

上述代码通过 getInfo 接口获取用户权限, 通过函数 filterRouter 过滤掉该角色不拥有的路由, 通过 concat 方法合并 routes 公共路由, 最后通过 resolve 返回

文件布局如下

在这里插入图片描述

下图为页面渲染的菜单(项目经理角色)

在这里插入图片描述

左侧菜单实现参考链接: Elemnt-UI + 递归组件实现后台管理系统左侧菜单

前端结合后端接口请求实现动态路由参考连接: 前端 + 接口请求实现 vue 动态路由

总结

在用户登录成功后从服务器获取用户的权限信息,在 vuex 的异步处理函数中过滤掉角色权限不存在的路由,使用 concat() 合并公共路由,最后使用 router.addRoutes(res) 动态添加可访问的路由。这样可以确保应用根据用户的权限动态加载相应的路由,增强安全性与灵活性。


点击全文阅读


本文链接:http://zhangshiyu.com/post/170934.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1