当前位置: 首页 > news >正文

前端 + 接口请求实现 vue 动态路由

前端 + 接口请求实现 vue 动态路由

在 Vue 应用中,通过前端结合后端接口请求来实现动态路由是一种常见且有效的权限控制方案。这种方法允许前端根据用户的角色和权限,动态生成和加载路由,而不是在应用启动时就固定所有的路由配置。

实现原理

  1. 定义静态路由配置:

    • 在项目的初始阶段,定义一套完整的路由配置,这些配置包含了所有可能的路由路径和相关的权限信息。
  2. 用户登录与鉴权:

    • 用户登录时,前端向后端发送请求验证用户的身份。
    • 服务器验证成功后,返回一个包含用户信息和权限的数据对象。
  3. 获取用户权限信息:

    • 前端根据登录时获得的令牌(如 JWT),再次向后端请求获取当前用户的权限信息。
    • 权限信息可能包括用户的角色、能够访问的资源等。
  4. 动态生成路由:

    • 前端根据从后端获取的权限信息,动态生成符合用户权限的路由表。
    • 这个过程可以通过递归算法处理路由配置树,根据用户的权限过滤掉无权访问的路由。
  5. 动态添加路由:

    • 使用 Vue Router 的 router.addRoutes(routes) 方法将生成的路由动态添加到路由实例中。
    • 这样只有经过权限验证的路由才会被添加,从而实现了权限控制。
  6. 动态渲染菜单:

    • 左侧菜单通常是基于生成的路由表来渲染的,因此只有用户有权访问的路由才会在菜单中显示。

优点

  1. 安全性:

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

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

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

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

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

示例

在前导航路由钩子 beforeEach 函数里发送接口请求获取路由信息

permission.js

// 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(() => {// 获取路由信息store.dispatch('GenerateRoutes').then((res) => {console.log('--------------', res);// 根据roles权限生成可访问的路由表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`) // 否则全部重定向到登录页}}
})

permission.js 文件需引入到 main.js 里

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

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

假设后端接口返回的路由权限如下

[{path: '/admin',meta: {title: "系统管理",},component: 'Layout',children: [{path: 'user',name: 'userIndex',meta: {title: "用户管理",},component: '/admin/user/index.vue'},{path: 'role',name: 'roleIndex',meta: {title: "角色管理",},component: '/admin/role/index.vue',children: [{path: 'add',name: 'addRole',meta: {title: "添加角色",},component: '/admin/user/index.vue'},{path: 'update',name: 'updateRole',meta: {title: "编辑角色",},component: '/admin/role/index.vue'}]}]},{path: '/tableEcho',meta: {title: "表格管理",},component: 'Layout',children: [{path: 'test',name: 'tableEchoIndex',meta: {title: "表格测试",},component: '/tableEcho/index.vue',children: [{path: 'add',name: 'addTable',hidden: true,meta: {title: "新增测试",},component: '/tableEcho/add.vue'}]},],},
]

vuex 处理数据

store/index.vue

// store/index.vue
import Vue from 'vue'
import Vuex from 'vuex'
import { routes, dynamicRoutes } from "@/router";
import { login, getInfo, logout, getRouters } from "@/api/user";
import { setStore, clearStore } from '@/utils/store';
import Layout from '@/Layout/index.vue'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']);}resolve();} else {reject(error);}}).catch(error => {reject(error);})})},GenerateRoutes({ commit }) {return new Promise((resolve, reject) => {// 向后端请求路由数据getRouters().then(res => {if (res.data.code === 0 || 200) {const sdata = JSON.parse(JSON.stringify(res.data.routes));console.log('sdata::: ', sdata);let newRouters = filterAsyncRouter(sdata);// 连接公共路由const sidebarRoutes = routes.concat([...newRouters]);commit('SET_ROUTE', sidebarRoutes);resolve(sidebarRoutes);} 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: {}
})const loadView = (view) => {// 路由懒加载return () => import(`@/views${view}`);
};function filterAsyncRouter(routes) {return routes.filter((route) => {if (Array.isArray(route.children) && route.children.length > 0) {// 如果该路由含有子路由时,递归调用该函数route.children = filterAsyncRouter(route.children);}if (route.component) {// Layout ParentView 组件特殊处理if (route.component === "Layout") {route.component = Layout;} else {// 路由组件懒加载route.component = loadView(route.component);}}return true;})
}

由于接口返回的 component 是字符串, 需手动封装函数转换成组件

公共路由如下

router/index.js

// router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Layout from '@/Layout/index.vue'Vue.use(VueRouter)// 公共路由
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},
]const router = new VueRouter({base: process.env.BASE_URL,routes
})export default router

文件结构如下
在这里插入图片描述
页面菜单渲染

在这里插入图片描述

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

前端单独实现动态路由参考连接: 前端单独实现 vue 动态路由

总结

通过以上步骤,你可以实现一个完整的动态路由权限管理系统:

  • 后端接口返回路由配置:获取用户的权限信息及路由信息。
  • 动态加载组件:使用异步组件方式加载指定路径的组件。
  • 动态添加路由:根据权限信息动态添加路由。
  • 路由守卫:使用 router.addRoutes()router.addRoute() 添加路由。

这样可以确保应用根据用户的权限动态加载相应的路由,增强安全性与灵活性。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • QTreeView模糊查询
  • 建模杂谈系列256 规则函数化改造
  • 【机器学习】--- 自监督学习
  • 202409011在飞凌的OK3588-C的核心板跑Rockchip原厂的Android12时挂载触摸屏ft5x06之后使用i2c-tools检测
  • sql server 分区表查询
  • JavaEE:网络初识
  • Spring Boot:现代化Java应用开发的艺术
  • Blazor静态服务端呈现(静态SSR)身份认证
  • 【Python笔记】PyCharm大模型项目环境配置
  • Qt 定时器-定时备份
  • 微信小程序中实现类似于 ECharts 的图表渲染及优化
  • 家电制造的隐形守护者:矫平机确保材料完美无瑕
  • gin基本使用
  • 探索Python的Excel世界:openpyxl的魔法之旅
  • 51单片机应用开发---数码管的控制应用
  • mockjs让前端开发独立于后端
  • Octave 入门
  • Rancher-k8s加速安装文档
  • React 快速上手 - 07 前端路由 react-router
  • 百度贴吧爬虫node+vue baidu_tieba_crawler
  • 半理解系列--Promise的进化史
  • 关于springcloud Gateway中的限流
  • 记录:CentOS7.2配置LNMP环境记录
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 开源SQL-on-Hadoop系统一览
  • 来,膜拜下android roadmap,强大的执行力
  • 前端性能优化——回流与重绘
  • 如何用vue打造一个移动端音乐播放器
  • 深度学习中的信息论知识详解
  • 一、python与pycharm的安装
  • 一道闭包题引发的思考
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • 移动端高清、多屏适配方案
  • # dbt source dbt source freshness命令详解
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • #ifdef 的技巧用法
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (9)目标检测_SSD的原理
  • (C语言)fgets与fputs函数详解
  • (Matlab)使用竞争神经网络实现数据聚类
  • (poj1.2.1)1970(筛选法模拟)
  • (Python) SOAP Web Service (HTTP POST)
  • (pytorch进阶之路)扩散概率模型
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (二十六)Java 数据结构
  • (附源码)c#+winform实现远程开机(广域网可用)
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (接口封装)
  • (六)库存超卖案例实战——使用mysql分布式锁解决“超卖”问题
  • (入门自用)--C++--抽象类--多态原理--虚表--1020
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • (转)nsfocus-绿盟科技笔试题目
  • .babyk勒索病毒解析:恶意更新如何威胁您的数据安全