前端单独实现 vue 动态路由

devtools/2024/11/14 23:13:25/

前端单独实现 vue 动态路由

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

动态路由的优点:

  • 安全性:

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

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

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

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

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

实现步骤

  • 定义静态路由配置:

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

    • 用户登录时,前端存储用户的权限信息(如角色、权限列表等)。
  • 动态生成路由:

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

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

代码示例

配置路由器

router/index.js

javascript">// router/index.js
import 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

javascript">// permission.js
import 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 {// 没有tokenif (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() 方法逐个添加路由

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

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

store/index.js

javascript">// store/index.js
import 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 含有当前角色的 roleTypeif (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://www.ppmy.cn/devtools/111371.html

相关文章

如何在 Selenium 中获取网络调用请求?

引言 捕获网络请求对于理解网站的工作方式以及传输的数据至关重要。Selenium 作为一种 Web 自动化工具,可以用于捕获网络请求。本文将讨论如何使用 Selenium 在 Java 中捕获网络请求并从网站检索数据。 我们可以使用浏览器开发者工具轻松捕获网络请求或日志。大多数现代 Web…

Redis访问工具

使用Redis存储缓存数据,如何通过Java去访问Redis? 防止后面看晕,先来张图。 1. Redis的客户端库 Redis的客户端库是Redis官方提供的,用于让Java等编程语言与Redis服务器进行通信的工具包。常见的Redis客户端库有多个&#xff0c…

【Linux 从基础到进阶】使用 Crontab 进行任务调度

使用 Crontab 进行任务调度 Crontab 是 Linux 和 Unix 系统中用于定时执行任务的一个重要工具,适用于自动化脚本执行、系统维护、备份等定时任务。通过 Crontab,可以让系统在指定的时间自动执行设定的命令或脚本,大大提高了系统管理和运维的效率。 本文将详细介绍如何使用…

【SOP】使用MMDeploy将MMAction2的模型转换为TensorRT

配置环境 MMAction2 1.2.0 配置MMAction2环境CUDA 11.8 安装CUDA,cuDNN,NCCLcuDNN 8.4.1 安装CUDA,cuDNN,NCCLTensorRT GA 8.6 配置TensorRT环境MMDeploy pip install mmdeploypytorch 2.0.1, torchvision 0.15.2 conda install …

三维坐标变换

(一些困惑梳理记录) “坐标转换”的区分 1、坐标系基底变换 2、目标描述向量变换 总的来说, A属于1、坐标系基底变换, B中所述方法,可用于1、坐标系基底变换,也可用于2、目标描述向量变换&#xff0c…

计算机网络(网络层)

网络层的主要任务是实现网络互联,进而实现数据包在个网络中间的传输。 网络层主要解决的问题 (1)网络层向运输层提供怎样的服务 (2)网络层寻址问题 (3)路由选择问题 网路层提供的两种服务 …

OpenCV结构分析与形状描述符(13)拟合椭圆函数fitEllipseDirect()的使用

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 围绕一组2D点拟合一个椭圆。 该函数计算出一个椭圆,该椭圆拟合一组2D点。它返回一个内切于该椭圆的旋转矩形。使用了由[91]提出的直接…

【vulhub】thinkphp5 2-rce 5.0.23-rce 5-rce 漏洞复现

2-rec 1.启动环境 cd /.../vulhub/thinkphp/2-rce # cd进入2-rce靶场文件环境下 docker-compose up -d # docker-compose启动靶场 docker ps -a # 查看开启的靶场信息2.访问192.168.146.136:8080网页 3.构造payload http://192.168.146.136:80…