前端实现动态路由(后端返回权限路由)

news2024/11/25 12:54:32

在这里插入图片描述

实现思路

1、前端定义静态路由(login登录页这种不需要权限的默认路由)
2、用户登陆时调接口获取用户信息,然后登录到首页
3、前后端定义好路由返回的格式
4、在路由导航钩子beforeEach中去调接口获取动态路由,递归处理该数据为前端可用的路由数据,使用router.addRouters()添加路由

步骤一:前端定义静态路由(login登录页这种不需要权限的默认路由)

router/index.js

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

// 解决重复点击路由报错的BUG
// 下面这段代码主要解决这个问题 :Uncaught (in promise) Error: Redirected when going from "/login" to "/index" via a navigation guard.
const originalPush = VueRouter.prototype.push
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}

// 定义好静态路由
const routes = [
  {
    path: '/login',
    name: 'login',
    component: () => import('../views/login'),
    hidden: true,
  },
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes,
})

export default router

步骤二:用户登陆时调接口获取用户信息,然后登录到首页

login/index.vue

methods: {
    login () {
      this.$refs.userForm.validate((valid) => {
        if (valid) {
          // 模拟登录接口去请求用户数据
          setTimeout(() => {
            // 这里的res就是模拟后台返回的用户数据
            const res = dynamicUserData.filter((item) => item.username === this.user.username)[0]
            console.log(res)
            // 存储用户的信息及token到vuex,并做sessionStorage持久化处理
            this.$store.commit('User/saveUserInfo',res)
            Message({ type: 'success', message: "登录成功", showClose: true, duration: 3000 })
            this.$router.push({ path: "/index" })
          }, 1000)
        } else return false
      })
    }
  }

附:vuex持久化处理:使用vuex-persistedstate插件将User仓库的内容存储到sessionStorage中

import Vue from 'vue'
import Vuex from 'vuex'
import User from './modules/user'
import permission from './modules/permission'
import createPersistedState from 'vuex-persistedstate'
Vue.use(Vuex)

export default new Vuex.Store({
  state: {},
  mutations: {},
  actions: {},
  modules: {
    User,
    permission,
  },
  plugins: [
    createPersistedState({
      storage: window.sessionStorage, // 可选sessionStorage localStorage
      reducer(val) {
        return {
          User: val.User,
        }
      },
    }),
  ],
})

步骤三:前后端定义好路由返回的格式

// 后台返回的数据结构
const dynamicUser = [
  {
    name: '管理员',
    avatar: 'https://sf3-ttcdn-tos.pstatp.com/img/user-avatar/ccb565eca95535ab2caac9f6129b8b7a~300x300.image',
    desc: '管理员 - admin',
    username: 'admin',
    password: '654321',
    token: 'rtVrM4PhiFK8PNopqWuSjsc1n02oKc3f',
    routes: [
      {
        id: 1,
        name: '/',
        path: '/',
        component: 'Layout',
        redirect: '/index',
        hidden: false,
        children: [{ name: 'index', path: '/index', meta: { title: 'index' }, component: 'index/index' }],
      },
      {
        id: 2,
        name: '/form',
        path: '/form',
        component: 'Layout',
        redirect: '/form/index',
        hidden: false,
        children: [{ name: '/form/index', path: '/form/index', meta: { title: 'form' }, component: 'form/index' }],
      },
      {
        id: 3,
        name: '/example',
        path: '/example',
        component: 'Layout',
        redirect: '/example/tree',
        meta: { title: 'example' },
        hidden: false,
        children: [
          { name: '/tree', path: '/example/tree', meta: { title: 'tree' }, component: 'tree/index' },
          { name: '/copy', path: '/example/copy', meta: { title: 'copy' }, component: 'tree/copy' },
        ],
      },
      {
        id: 4,
        name: '/table',
        path: '/table',
        component: 'Layout',
        redirect: '/table/index',
        hidden: false,
        children: [{ name: '/table/index', path: '/table/index', meta: { title: 'table' }, component: 'table/index' }],
      },
      {
        id: 5,
        name: '/admin',
        path: '/admin',
        component: 'Layout',
        redirect: '/admin/index',
        hidden: false,
        children: [{ name: '/admin/index', path: '/admin/index', meta: { title: 'admin' }, component: 'admin/index' }],
      },
      {
        id: 6,
        name: '/people',
        path: '/people',
        component: 'Layout',
        redirect: '/people/index',
        hidden: false,
        children: [{ name: '/people/index', path: '/people/index', meta: { title: 'people' }, component: 'people/index' }],
      },
    ],
  },
  {
    name: '普通用户',
    avatar: 'https://sf1-ttcdn-tos.pstatp.com/img/user-avatar/6364348965908f03e6a2dd188816e927~300x300.image',
    desc: '普通用户 - people',
    username: 'people',
    password: '123456',
    token: '4es8eyDwznXrCX3b3439EmTFnIkrBYWh',
    routes: [
      {
        id: 1,
        name: '/',
        path: '/',
        component: 'Layout',
        redirect: '/index',
        hidden: false,
        children: [{ name: 'index', path: '/index', meta: { title: 'index' }, component: 'index/index' }],
      },
      {
        id: 2,
        name: '/form',
        path: '/form',
        component: 'Layout',
        redirect: '/form/index',
        hidden: false,
        children: [{ name: '/form/index', path: '/form/index', meta: { title: 'form' }, component: 'form/index' }],
      },
      {
        id: 3,
        name: '/example',
        path: '/example',
        component: 'Layout',
        redirect: '/example/tree',
        meta: { title: 'example' },
        hidden: false,
        children: [
          { name: '/tree', path: '/example/tree', meta: { title: 'tree' }, component: 'tree/index' },
          { name: '/copy', path: '/example/copy', meta: { title: 'copy' }, component: 'tree/copy' },
        ],
      },
      {
        id: 4,
        name: '/table',
        path: '/table',
        component: 'Layout',
        redirect: '/table/index',
        hidden: false,
        children: [{ name: '/table/index', path: '/table/index', meta: { title: 'table' }, component: 'table/index' }],
      },
      {
        id: 6,
        name: '/people',
        path: '/people',
        component: 'Layout',
        redirect: '/people/index',
        hidden: false,
        children: [{ name: '/people/index', path: '/people/index', meta: { title: 'people' }, component: 'people/index' }],
      },
    ],
  },
]

export default dynamicUser

步骤四:路由导航钩子beforeEach处理

路由钩子逻辑:

是否为白名单:
	是:  直接进入
	不是:判断是否有token
		无token:跳转到login登录页
		有token:判断用户路由状态(是否有路由)
			有路由: 直接进入
			无路由: 调接口获取动态路由
					递归处理返回的路由
					将递归处理好的路由存储到vuex,设置用户路由状态为true
					使用router.addRouters()添加路由

路由导航守卫:

import router from './index'
import Layout from '../layout/index'
import NProgress from 'nprogress' // progress bar
import store from '@/store'
import menu from '@/mock/menu.js'

// 路由拼接
function loadView(view) {
  return () => import(`@/views/${view}`)
}
// 路由过滤   遍历路由 转换为组件对象和路径
function filterASyncRoutes(data) {
  // console.log(data)
  const routes = data.filter(item => {
    if (item['component'] === 'Layout') {
      item.component = Layout
    } else {
      item['component'] = loadView(item['component'])
    }
    // 路由递归,转换组件对象和路径
    if (item['children'] && item['children'].length > 0) {
      item['children'] = filterASyncRoutes(item.children)
    }
    return true
  })
  // 排序
  routes.sort((a, b) => a['id'] - b['id'])
  return routes
}

NProgress.configure({ showSpinner: false }) // NProgress Configuration

// 白名单页面直接进入
const whiteList = ['/login']

router.beforeEach((to, from, next) => {
  NProgress.start()
  // 白名单页面,不管是否有token,是否登录都直接进入
  if (whiteList.indexOf(to.path) !== -1) {
    next()
    return false
  }
  // 有token(代表了有用户信息,但是不确定有没有路由信息)
  if (store.state.User.token) {
    // 判断当前用户是否是登录状态, 是登录状态则一定有路由,直接放行,不是登录状态则去获取路由菜单登录
    // 刷新时store.state.routerList.hasRoutes会重置为false,重新去获取 异步路由
    if (!store.state.routerList.hasRoutes) {
      setTimeout(() => {
        const res = menu.filter(item => item.token === store.state.User.token)[0].routes
        const asyncRouter = filterASyncRoutes(res) // 递归处理后台返回的路由
        store.commit('routerList/setRouterList', asyncRouter) // 存储到异步的路由到vuex
        store.commit('routerList/setHasRoutes', true) // 设置登录状态为true
        router.addRoutes(asyncRouter) // 动态添加可访问路由表
        next({ ...to, replace: true }) // hack方法 router.addRoutes之后的next()可能会失效,可能next()的时候路由并没有完全add完成 通过next(to)解决
      }, 500)
    } else {
      next() //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的全面会自动进入404页面
    }
  }else {
    next({path:'/login'})
  }
})

router.afterEach(() => {
  // finish progress bar
  NProgress.done()
})

注意
这里将异步路由和路由状态的数据放到了routerList.js里面,但是这里是没做缓存的
原因:让用户刷新时这里的数据重置,然后重新走路由钩子的时候就会去调接口获取路由信息。
(为什么不把路由放到sessionStorage中? 原因是路由数组中的component无法保存到sessionStorage中,() => import(@/views/${userAuth.component}) 这种保存不到sessionStorage中 )

获取的用户信息需要存储在vuex并做持久化处理,但是获取的菜单存在vuex中不做持久化处理,让用户在刷新时会清空,重新走获取菜单的接口,也就是下面这段代码

if (!store.state.routerList.hasRoutes) {
 setTimeout(() => {
    const res = menu.filter(item => item.token === store.state.User.token)[0].routes
    const asyncRouter = filterASyncRoutes(res) // 递归处理后台返回的路由
    store.commit('routerList/setRouterList', asyncRouter) // 存储到异步的路由到vuex
    store.commit('routerList/setHasRoutes', true) // 设置登录状态为true
    router.addRoutes(asyncRouter) // 动态添加可访问路由表
    next({ ...to, replace: true }) // hack方法 router.addRoutes之后的next()可能会失效,可能next()的时候路由并没有完全add完成 通过next(to)解决
  }, 500)
}

我公司目前就是采用后端返回路由做的权限管理

希望能帮到你哦

参考代码在github 给我来个star

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/970082.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AtCoder Beginner Contest 315 Ex. Typical Convolution Problem(分治NTT/全在线卷积)

题目 给定长为n(n<2e5)的序列a&#xff0c;第i个数ai(0<ai<998244353) 求序列f&#xff0c;满足式子如下&#xff1a; 思路来源 jiangly代码/力扣群友tdzl2003/propane/自己的乱搞 题解 分治NTT&#xff0c;考虑[l,mid]对[mid1,r]的贡献&#xff0c; 但是&#x…

RT-Thread I/O设备模型(二)

访问I/O设备 应用程序通过I/O设备管理接口来访问硬件设备&#xff0c;当设备驱动程序实现后&#xff0c;应用程序就可以访问该硬件。I/O设备管理接口与I/O设备的操作方法映射关系如下&#xff1a; 查找设备 应用程序根据设备名称获取设备句柄&#xff0c;进而操作设备。 rt_…

MybatisPlus基本配置查询操作

无侵入&#xff1a;只做增强不做改变&#xff0c;引入它不会对现有工程产生影响&#xff0c;如丝般顺滑损耗小&#xff1a;启动即会自动注入基本 CURD&#xff0c;性能基本无损耗&#xff0c;直接面向对象操作强大的 CRUD 操作&#xff1a;内置通用 Mapper、通用 Service&#…

手把手教你用Vite构建第一个Vue3项目

写在前面 在之前的文章中写过“如何创建第一个vue项目”&#xff0c;但那篇文章写的是创建vue2的 项目。 传送门如何创建第一个vue项目 打开Vue.js官网:https://cn.vuejs.org/&#xff0c;我们会发现Vue 2 将于 2023 年 12 月 31 日停止维护 虽然Vue2的项目还不少&#xff0…

day5 qt

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);timer_idthis->startTimer(100);//啓動一個定時器 每100ms發送一次信號ui->Edit1->setPlaceholderTex…

Leetcode:349. 两个数组的交集【题解超详细】

题目 给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 难度&#xff1a;简单 题目链接&#xff1a;349.两个数组的交集 示例 1&#xff1a; 输入&#xff1a;nums1 [1,2,2,1], nums2 [2,…

CentOS7上源码安装Redis6

CentOS7上源码安装Redis6 安装依赖开始安装下载安装包解压安装包编译源代码修改配置文件 启动并连接启动redis服务器本地连接远程连接 下篇预告 安装依赖 首先我们是源码包安装我们需要安装C语言编译器&#xff0c;顺便下载wget&#xff1a; yum install -y gcc wget开始安装…

Ab3d.PowerToys 11.0.8614 Crack

版本 11.0.8614 修补程序 使用 MouseCameraController 移动相机时防止旋转 FreeCamera。 版本 11.0.8585 重大更改&#xff1a;由于专利问题删除了 ViewCubeCameraController - 请联系支持人员以获取更多信息以及如果您想继续使用此控件。添加了 CameraNavigationCircles 控件…

第12节——生命周期

一、概念 生命周期指 React 组件从装载至卸载的全过程&#xff0c;这个过程内置多个函数供开发者在组件的不同阶段执行需要的逻辑。 状态组件主要通过 3 个生命周期阶段来管理&#xff0c;分别是 挂载阶段&#xff08;MOUNTING&#xff09;&#xff0c;更新阶段&#xff08;U…

AJAX学习笔记3练习

AJAX学习笔记2发送Post请求_biubiubiu0706的博客-CSDN博客 1.验证用户名是否可用 需求,用户输入用户名,失去焦点-->onblur失去焦点事件,发送AJAX POST请求,验证用户名是否可用 新建表 前端页面 WEB-INF下新建lib包引入依赖,要用JDBC 后端代码 package com.web;import jav…

ModaHub魔搭社区:自动化机器学习框架AutoML

AutoML 自动化机器学习AutoML 是机器学习中一个相对较新的领域,它主要将机器学习中所有耗时过程自动化,如数据预处理、最佳算法选择、超参数调整等,这样可节约大量时间在建立机器学习模型过程中。 自动机器学习 AutoML: 对于 ,令 表示特征向量, 表示对应的目标值。给定…

基于SpringCloudAlibaba实现的NacosConfig

概述 Nacos除了实现了服务的注册发现之外&#xff0c;还将配置中心功能整合在了一起。通过Nacos的配置管理功能&#xff0c;我们可以将整个架构体系内的所有配置都集中在Nacos中存储。这样做的好处主要有以下几点&#xff1a; 分离的多环境配置&#xff0c;可以更灵活的管理权…

基于Java+SpringBoot+Vue前后端分离大学生智能消费记账系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

基于Java+SpringBoot+Vue前后端分离农商对接系统设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

机器人中的数值优化(十三)——QP二次规划

本系列文章主要是我在学习《数值优化》过程中的一些笔记和相关思考&#xff0c;主要的学习资料是深蓝学院的课程《机器人中的数值优化》和高立编著的《数值最优化方法》等&#xff0c;本系列文章篇数较多&#xff0c;不定期更新&#xff0c;上半部分介绍无约束优化&#xff0c;…

MLOps:掌握机器学习部署:Docker、Kubernetes、Helm 现代 Web 框架

介绍&#xff1a; 在机器学习的动态世界中&#xff0c;从开发模型到将其投入生产的过程通常被认为是复杂且多方面的。 然而&#xff0c;随着 Docker、Kubernetes 等工具以及 FastAPI、Streamlit 和 Gradio 等用户友好的 Web 框架的出现&#xff0c;这一过程变得比以往更加简化…

CS420 课程笔记 P3 - 计数系统基础和 Hex, Decimal, Binary 进制

文章目录 IntroductionInspirationWhy base systemsBinary & HexCounting in binaryAdditional resources Introduction 笔记作者 tips&#xff1a;这一节是关于进制的讲解&#xff0c;推荐观看原视频或学会二进制的读者跳过这一篇&#xff01; 本节课将介绍基本的计算机系…

台球击球角度公式. 包含数学推导

第一步. 物理来分析. 第二步. 数学计算.

【jsvue】联合gtp仿写一个简单的vue框架,以此深度学习JavaScript

用 gtp 学习 Vue 生命周期的原理 lifecycle.js function Vue(options) {// 将选项保存到实例的 $options 属性中this.$options options;// 若存在 beforeCreate 钩子函数&#xff0c;则调用之if (typeof options.beforeCreate function) {options.beforeCreate.call(this);…

Qt 5.15编译及集成Crypto++ 8.7.0笔记

一、背景 为使用AES加密库&#xff08;AES/CBC加解密&#xff09;&#xff0c;选用Crypto 库&#xff08;官网&#xff09;。   最新Crypto C库依次为&#xff1a;8.8.0版本&#xff08;2023-6-25&#xff09;、8.7.0&#xff08;2022-8-7&#xff09;和8.6.0&#xff08;202…