Vue实现动态路由

动态路由设置一般有两种:

**一、简单的角色路由设置(前端控制)**‌

这种方式适用于角色较少且权限相对固定的场景。前端预先定义好所有可能的路由和对应的角色权限,然后根据用户登录时获取的角色信息来筛选出用户可访问的路由。

**代码实现:**‌

1‌、**路由配置文件 (router/index.js)**‌:定义静态路由和异步路由(权限路由)。

javascript 复制代码
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Layout from '@/layout'
    import store from '@/store' // 假设使用 Vuex 存储用户角色

    Vue.use(VueRouter)

    // 静态路由,无需权限即可访问
    export const constantRoutes = [
      {
        path: '/login',
        component: () => import('@/views/login'),
        hidden: true
      },
      {
        path: '/404',
        component: () => import('@/views/404'),
        hidden: true
      }
    ]

    // 异步路由,根据角色权限加载
    export const asyncRoutes = [
      {
        path: '/permission',
        component: Layout,
        redirect: '/permission/page',
        alwaysShow: true,
        name: 'Permission',
        meta: {
          title: '权限管理',
          roles: ['admin', 'editor'] // 普通的用户角色
        },
        children: [
          {
            path: 'page',
            component: () => import('@/views/permission/page'),
            name: 'PagePermission',
            meta: {
              title: '页面权限',
              roles: ['editor'] // editor角色的用户才能访问该页面
            }
          },
          {
            path: 'role',
            component: () => import('@/views/permission/role'),
            name: 'RolePermission',
            meta: {
              title: '角色权限',
              roles: ['admin'] // admin角色的用户才能访问该页面
            }
          }
        ]
      },
      {
        path: '/dashboard',
        component: Layout,
        name: 'Dashboard',
        meta: {
          title: '仪表盘',
          roles: ['admin', 'user'] // admin 和 user 都可以访问
        }
      }
      // 可以继续添加更多异步路由...
    ]

    const createRouter = () => new VueRouter({
      mode: 'history',
      scrollBehavior: () => ({ y: 0 }),
      routes: constantRoutes
    })

    const router = createRouter()

    // 用于重置路由的方法(例如用户登出后)
    export function resetRouter() {
      const newRouter = createRouter()
      router.matcher = newRouter.matcher // 重置路由匹配器
    }

    export default router
    

2‌、**权限过滤工具 (utils/asyncRouter.js)**‌:根据用户角色过滤异步路由。

javascript 复制代码
    /**
     * 判断当前用户是否有访问该路由的权限
     * @param {Array} roles - 用户的角色列表
     * @param {Object} route - 路由对象
     * @returns {Boolean}
     */
    export function hasPermission(roles, route) {
      if (route.meta && route.meta.roles) {
        // 如果路由定义了需要的角色,则检查用户角色是否包含在内
        return roles.some(role => route.meta.roles.includes(role))
      } else {
        // 如果没有定义角色,则默认允许访问
        return true
      }
    }

    /**
     * 递归过滤异步路由表,筛选出用户有权限访问的路由
     * @param {Array} routes - 异步路由列表
     * @param {Array} roles - 用户的角色列表
     * @returns {Array}
     */
    export function filterAsyncRoutes(routes, roles) {
      const res = []
      routes.forEach(route => {
        const tmp = { ...route }
        if (hasPermission(roles, tmp)) {
          // 如果有子路由,递归过滤子路由
          if (tmp.children) {
            tmp.children = filterAsyncRoutes(tmp.children, roles)
          }
          res.push(tmp)
        }
      })
      return res
    }
    

‌3、**路由守卫 (permission.js)**‌:在路由跳转前进行权限判断。

javascript 复制代码
    import router from './router'
    import store from './store'
    import { getToken } from '@/utils/auth' // 假设有一个获取 token 的工具函数
    import { filterAsyncRoutes } from '@/utils/asyncRouter' // 引入权限过滤工具
    import NProgress from 'nprogress' // 进度条
    import 'nprogress/nprogress.css'

    NProgress.configure({ showSpinner: false })

    const whiteList = ['/login'] // 白名单,不需要登录即可访问的路由

    router.beforeEach(async (to, from, next) => {
      // 开始进度条
      NProgress.start()

      // 获取 token
      const token = getToken()

      if (token) {
        // 如果已经登录
        if (to.path === '/login') {
          // 如果访问的是登录页,则跳转到首页
          next({ path: '/' })
          NProgress.done()
        } else {
          // 检查用户信息是否已获取
          const hasRoles = store.getters.roles && store.getters.roles.length > 0
          if (hasRoles) {
            // 如果用户角色已存在,则直接进入
            next()
          } else {
            try {
              // 获取用户信息(通常包含角色)
              const { roles } = await store.dispatch('user/getUserInfo')
              // 根据用户角色过滤路由
              const accessRoutes = filterAsyncRoutes(asyncRoutes, roles)
              // 动态添加路由
              router.addRoutes(accessRoutes)
              // 将过滤后的路由存入 store(可选,用于菜单渲染等)
              store.commit('permission/SET_ROUTES', accessRoutes)
              // 继续导航
              next({ ...to, replace: true })
            } catch (error) {
              // 获取用户信息失败,清除 token 并跳转到登录页
              await store.dispatch('user/resetToken')
              next(`/login?redirect=${to.path}`)
              NProgress.done()
            }
          }
        }
      } else {
        // 如果未登录
        if (whiteList.indexOf(to.path) !== -1) {
          // 如果是白名单中的路由,则直接进入
          next()
        } else {
          // 否则跳转到登录页
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    })

    // 路由结束
    router.afterEach(() => {
      NProgress.done()
    })
    

**二、复杂的路由权限设置(后端控制)**‌

这种方式适用于权限复杂、角色多变的系统。前端不预先定义所有路由,而是由后端返回用户当前拥有的路由列表,前端根据这个列表动态添加路由。

‌**代码实现:**‌

‌1、**路由配置文件 (router/index.js)**‌:只定义静态路由和一个空的动态路由数组

javascript 复制代码
    import Vue from 'vue'
    import VueRouter from 'vue-router'
    import Layout from '@/layout'
    import store from '@/store'

    Vue.use(VueRouter)

    // 静态路由,无需权限即可访问
    export const constantRoutes = [
      {
        path: '/login',
        component: () => import('@/views/login'),
        hidden: true
      },
      {
        path: '/404',
        component: () => import('@/views/404'),
        hidden: true
      }
    ]

    // 动态路由,由后端返回
    export const asyncRoutes = []

    const createRouter = () => new VueRouter({
      mode: 'history',
      scrollBehavior: () => ({ y: 0 }),
      routes: constantRoutes
    })

    const router = createRouter()

    export function resetRouter() {
      const newRouter = createRouter()
      router.matcher = newRouter.matcher
    }

    export default router
    

2‌、**API 请求工具 (api/user.js)**‌:用于获取用户权限路由列表。

javascript 复制代码
    import request from '@/utils/request'

    // 获取用户信息(包含角色)
    export function getUserInfo() {
      return request({
        url: '/user/info',
        method: 'get'
      })
    }

    // 获取用户权限路由列表(后端返回)
    export function getPermissionRoutes() {
      return request({
        url: '/user/permission/routes',
        method: 'get'
      })
    }
    

**3、路由守卫 (permission.js)**‌:在用户登录后,调用后端接口获取路由列表并动态添加。

javascript 复制代码
    import router from './router'
    import store from './store'
    import { getToken } from '@/utils/auth'
    import { getPermissionRoutes } from '@/api/user' // 引入获取权限路由的 API
    import NProgress from 'nprogress'
    import 'nprogress/nprogress.css'

    NProgress.configure({ showSpinner: false })

    const whiteList = ['/login']

    router.beforeEach(async (to, from, next) => {
      NProgress.start()

      const token = getToken()

      if (token) {
        if (to.path === '/login') {
          next({ path: '/' })
          NProgress.done()
        } else {
          const hasRoles = store.getters.roles && store.getters.roles.length > 0
          if (hasRoles) {
            next()
          } else {
            try {
              // 获取用户信息(通常包含角色)
              const { roles } = await store.dispatch('user/getUserInfo')
              // 获取后端返回的权限路由列表
              const routeData = await getPermissionRoutes()
              // 假设后端返回的路由数据结构是 { routes: [...], ... }
              const accessRoutes = routeData.routes || []
              // 动态添加路由
              router.addRoutes(accessRoutes)
              // 将过滤后的路由存入 store(可选,用于菜单渲染等)
              store.commit('permission/SET_ROUTES', accessRoutes)
              next({ ...to, replace: true })
            } catch (error) {
              await store.dispatch('user/resetToken')
              next(`/login?redirect=${to.path}`)
              NProgress.done()
            }
          }
        }
      } else {
        if (whiteList.indexOf(to.path) !== -1) {
          next()
        } else {
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
    })

    router.afterEach(() => {
      NProgress.done()
    })
    

总结说明:‌

一 (前端控制)‌:通过在路由配置中定义 meta.roles 字段来标记路由所需的角色。在路由守卫中,根据用户角色调用 filterAsyncRoutes 函数过滤出用户可访问的路由,并使用 router.addRoutes 动态添加。这种方式简单直接,适合权限结构相对固定的场景。

二 (后端控制)‌:前端不预先定义路由,而是通过调用后端 API 获取用户权限对应的路由列表。后端根据用户角色和权限规则生成路由配置(通常包含 path, component, meta 等信息),前端接收到后直接使用 router.addRoutes 添加。这种方式更灵活,适合权限复杂、需要频繁调整的系统,但需要后端配合提供路由数据接口。
两种方式的核心都是利用 router.addRoutes (Vue Router 3.x) 或 router.addRoute (Vue Router 4.x) 来动态添加路由,并结合路由守卫进行权限判断
注意:vue是单页面应用程序,所以页面一刷新数据部分数据也会跟着丢失,所以我们需要将store中的数据存储到本地,才能保证路由不丢失

相关推荐
xiao阿娜的妙妙屋12 小时前
当AI Agent开始自我进化,我们普通人应该怎么办?
前端
sudo_jin2 小时前
从“谁调用指向谁”到“手写Bind源码”,彻底搞懂JavaScript的this机制
前端·javascript
小蜜蜂dry2 小时前
nestjs实战-登录、鉴权(二)
前端·后端·nestjs
全栈王校长2 小时前
Nest 文件上传 - 就是增强版的 el-upload
前端·后端·nestjs
ZC跨境爬虫2 小时前
海南大学交友平台开发实战 day10(后端向前端输出_前端读取数据全流程联调+日志调试落地)
前端·python·sqlite·html·状态模式
xiaotao1312 小时前
CSS中的Grid 布局
前端·css
cc_heart2 小时前
antdv-next/x:面向 Vue 的 AI 组件体系
前端·javascript·vue.js
Ruihong2 小时前
一文看懂:Vue3 watch 用 VuReact 转成 React 长啥样
vue.js·react.js
竹林8182 小时前
RainbowKit快速集成多链钱包连接:从“一键连接”到“多链切换”的实战踩坑
前端·javascript