vue实现动态路由详细

目录
  • 一、前端控制
    • 1、在router.js文件(把静态路由和动态路由分别写在router.js)
    • 2、store/permission.js(在vuex维护一个state,通过配角色来控制菜单显不显示)
    • 3、src/permission.js
    • 4、侧边栏的可以从vuex里面取数据来进行渲染
  • 二、后端控制路由
    • 1、store/permission.js,在vuex里面发送请求获取数据
    • 2、整理一份数据结构,存到表里
    • 3、写一个转化方法,把获取到的数据转换成router结构

主流的实现方式:

简单聊一下两种方式的优势,毕竟如果你从来没做过,说再多也看不明白,还是得看代码

前端控制

  • 不用后端帮助,路由表维护在前端
  • 逻辑相对比较简单,比较容易上手

后端控制

  • 相对更安全一点
  • 路由表维护在数据库

一、前端控制

思路:在路由配置里,通过meta属性,扩展权限相关的字段,在路由守卫里通过判断这个权限标识,实现路由的动态增加,及页面跳转;如:我们增加一个role字段来控制角色

具体方案:

1、根据登录用户的账号,返回前端用户的角色

2、前端根据用户的角色,跟路由表的meta.role进行匹配

3、讲匹配到的路由形成可访问路由

核心代码逻辑

1、在router.js文件(把静态路由和动态路由分别写在router.js)

import Vue from 'vue'
import Router from 'vue-router'

Vue.use(Router)

import Layout from '@/layout'

// constantRoutes  静态路由,主要是登录页、404页等不需要动态的路由
export const constantRoutes = [
  {
    path: '/redirect',
    component: Layout,
    hidden: true,
    children: [
      {
        path: '/redirect/:path*',
        component: () => import('@/views/redirect/index')
      }
    ]
  },
  {
    path: '/login',
    component: () => import('@/views/login/index'),
    hidden: true
  },
  {
    path: '/404',
    component: () => import('@/views/error-page/404'),
    hidden: true
  },
  {
    path: '/401',
    component: () => import('@/views/error-page/401'),
    hidden: true
  }
] 

// asyncRoutes 动态路由
export const asyncRoutes = [
  {
    path: '/permission',
    component: Layout,
    redirect: '/permission/page',
    alwaysShow: true,
    name: 'Permission',
    meta: {
      title: 'Permission',
      icon: 'lock',
      // 核心代码,可以通过配的角色来进行遍历,从而是否展示
      // 这个意思就是admin、editor这两个角色,这个菜单是可以显示
      roles: ['admin', 'editor']
    },
    children: [
      {
        path: 'page',
        component: () => import('@/views/permission/page'),
        name: 'PagePermission',
        meta: {
          title: 'Page Permission',
          // 这个意思就是只有admin能展示
          roles: ['admin']
        }
      }
     ]
    }
]

const createRouter = () => new Router({
  scrollBehavior: () => ({ y: 0 }),
  routes: constantRoutes
})

const router = createRouter()

// 这个是重置路由用的,很有用,别看这么几行代码
export function resetRouter() {
  const newRouter = createRouter()
  router.matcher = newRouter.matcher
}

export default router

2、store/permission.js(在vuex维护一个state,通过配角色来控制菜单显不显示)

import { asyncRoutes, constantRoutes } from '@/router'

// 这个方法是用来把角色和route.meta.role来进行匹配
function hasPermission(roles, route) {
  if (route.meta && route.meta.roles) {
    return roles.some(role => route.meta.roles.includes(role))
  } else {
    return true
  }
}

// 这个方法是通过递归来遍历路由,把有权限的路由给遍历出来
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
}

const state = {
  routes: [],
  addRoutes: []
}

const mutations = {
  SET_ROUTES: (state, routes) => {
    // 这个地方维护了两个状态一个是addRouters,一个是routes
    state.addRoutes = routes
    state.routes = constantRoutes.concat(routes)
  }
}

const actions = {
  generateRoutes({ commit }, roles) {
    return new Promise(resolve => {
      let accessedRoutes
      if (roles.includes('admin')) {
        accessedRoutes = asyncRoutes || []
      } else {
        // 核心代码,把路由和获取到的角色(后台获取的)传进去进行匹配
        accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
      }
      // 把匹配完有权限的路由给set到vuex里面
      commit('SET_ROUTES', accessedRoutes)
      resolve(accessedRoutes)
    })
  }
}

export default {
  namespaced: true,
  state,
  mutations,
  actions
}

3、src/permission.js

(新建一个路由守卫函数,可以在main.js,也可以抽离出来一个文件)

这里面的代码主要是控制路由跳转之前,先查一下有哪些可访问的路由,登录以后跳转的逻辑可以在这个地方写

// permission.js
router.beforeEach((to, from, next) => {
  if (store.getters.token) { // 判断是否有token
    if (to.path === '/login') {
      next({ path: '/' });
    } else {
        // 判断当前用户是否已拉取完user_info信息
      if (store.getters.roles.length === 0) {
        store.dispatch('GetInfo').then(res => { // 拉取info
          const roles = res.data.role;
          // 把获取到的role传进去进行匹配,生成可以访问的路由
          store.dispatch('GenerateRoutes', { roles }).then(() => {
            // 动态添加可访问路由表(核心代码,没有它啥也干不了)
            router.addRoutes(store.getters.addRouters)

            // hack方法 确保addRoutes已完成
            next({ ...to, replace: true })
          })
        }).catch(err => {
          console.log(err);
        });
      } else {
        next() //当有用户权限的时候,说明所有可访问路由已生成 如访问没权限的全面会自动进入404页面
      }
    }
  } else {
    if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
      next();
    } else {
      next('/login'); // 否则全部重定向到登录页
    }
  }
})

4、侧边栏的可以从vuex里面取数据来进行渲染

核心代码是从router取可以用的路由对象,来进行侧边栏的渲染,不管是前端动态加载还是后端动态加载路由,这个代码都是一样的

<!-- layout/components/siderbar.vue -->
<el-menu
:default-active="activeMenu"
:collapse="isCollapse"
:background-color="variables.menuBg"
:text-color="variables.menuText"
:unique-opened="false"
:active-text-color="variables.menuActiveText"
:collapse-transition="false"
mode="vertical"
>
    // 把取到的路由进行循环作为参数传给子组件
    <sidebar-item v-for="route in routes" :key="route.path" :item="route" :base-path="route.path" />
</el-menu>
// 获取有权限的路由
routes() {
  return this.$router.options.routes
}

<!-- layout/components/siderbarItem.vue -->
  <template slot="title">
    <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
  </template>
  <sidebar-item
    v-for="child in item.children"
    :key="child.path"
    :is-nest="true"
    :item="child"
    :base-path="resolvePath(child.path)"
    class="nest-menu"
  />

  props: {
    // route object
    item: {
      type: Object,
      required: true
    },
    isNest: {
      type: Boolean,
      default: false
    },
    basePath: {
      type: String,
      default: ''
    }
  }

前端控制路由,逻辑相对简单,后端只需要存这个用户的角色就可以了,前端拿用户的角色进行匹配。但是如果新增角色,就会非常痛苦,每一个都要加。

二、后端控制路由

后端控制大致思路是:路由配置放在数据库表里,用户登录成功后,根据角色权限,把有权限的菜单传给前端,前端格式化成页面路由识别的结构,再渲染到页面菜单上;

  • 用户登录以后,后端根据该用户的角色,直接生成可访问的路由数据,注意这个地方是数据
  • 前端根据后端返回的路由数据,转成自己需要的路由结构

具体逻辑:

  • router.js里面只放一些静态的路由,login、404之类
  • 整理一份数据结构,存到表里
  • 从后端获取路由数据,写一个数据转换的方法,讲数据转成可访问的路由
  • 也是维护一个vuex状态,将转换好的路由存到vuex里面
  • 侧边栏也是从路由取数据进行渲染

因为前段控制和后端控制,后面的流程大部分都是一样的,所以这个地方只看看前面不一样的流程:

1、store/permission.js,在vuex里面发送请求获取数据

GenerateRoutes({ commit }, data) {
  return new Promise((resolve, reject) => {
    getRoute(data).then(res => {
     // 将获取到的数据进行一个转换,然后存到vuex里
      const accessedRouters = arrayToMenu(res.data)
      accessedRouters.concat([{ path: '*', redirect: '/404', hidden: true }])
      commit('SET_ROUTERS', accessedRouters)
      resolve()
    }).catch(error => {
      reject(error)
    })
  })
}

2、整理一份数据结构,存到表里

// 页面路由格式
{
    path: '/form',
    component: Layout,
    children: [
      {
        path: 'index',
        name: 'Form',
        component: () => import('@/views/form/index'),
        meta: { title: 'Form', icon: 'form' }
      }
    ]
}

// 整理后的数据格式
// 一级菜单
// parentId为0的就可以当做一级菜单,id最好是可以选4位数,至于为什么等你开发项目的时候就知道了
{
    id: 1300
    parentId: 0
    title: "菜单管理"
    path: "/menu"
    hidden: false
    component: null
    hidden: false
    name: "menu"
},

// 二级菜单
// parentId不为0的,就可以拿parentId跟一级菜单的id去匹配,匹配上的就push到children里面
{
    id: 1307
    parentId: 1300
    title: "子菜单"
    hidden: false
    path: "menuItem"
    component: "menu/menuItem" // 要跟本地的文件地址匹配上
    hidden: false
    name: "menuItem"
}

3、写一个转化方法,把获取到的数据转换成router结构

export function arrayToMenu(array) {
  const nodes = []
  // 获取顶级节点
  for (let i = 0; i < array.length; i++) {
    const row = array[i]
    // 这个exists方法就是判断下有没有子级
    if (!exists(array, row.parentId)) {
      nodes.push({
        path: row.path, // 路由地址
        hidden: row.hidden, // 全部true就行,如果后端没配
        component: Layout, // 一般就是匹配你文件的component
        name: row.name, // 路由名称
        meta: { title: row.title, icon: row.name }, // title就是显示的名字
        id: row.id, // 路由的id
        redirect: 'noredirect'
      })
    }
  }
  const toDo = Array.from(nodes)
  while (toDo.length) {
    const node = toDo.shift()
    // 获取子节点
    for (let i = 0; i < array.length; i++) {
      const row = array[i]
      // parentId等于哪个父级的id,就push到哪个
      if (row.parentId === node.id) {
        const child = {
          path: row.path,
          name: row.name,
          hidden: row.hidden,
          // 核心代码,因为二级路由的component是需要匹配页面的
          component: require('@/views/' + row.component + '/index.vue'),
          meta: { title: row.title, icon: row.name },
          id: row.id
        }
        if (node.children) {
          node.children.push(child)
        } else {
          node.children = [child]
        }
        toDo.push(child)
      }
    }
  }
  return nodes
}
// 看下有没有子级
function exists(rows, parentId) {
  for (let i = 0; i < rows.length; i++) {
    if (rows[i].id === parentId) return true
  }
  return false
}

到此这篇关于vue实现动态路由详细的文章就介绍到这了,更多相关vue实现动态路由内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 解决vue动态路由异步加载import组件,加载不到module的问题

    报错信息应该是这样的 webpackEmptyContext (eval at ./src/store/modules sync recursive (0.js:10), <anonymous>:2:10) 路由信息由后端给出,那么前端需要动态加载路由,同时component的路径也是后端给出,但是动态加载该路径会报错 如: // 假如path = '@/views/user' const com = () => import(path) // 这样会报错哦 const com2 = (

  • vue自定义指令和动态路由实现权限控制

    功能概述: 根据后端返回接口,实现路由动态显示 实现按钮(HTML元素)级别权限控制 涉及知识点: 路由守卫 Vuex使用 Vue自定义指令 导航守卫 前端工程采用Github开源项目Vue-element-admin作为模板,该项目地址:Github | Vue-element-admin. 在Vue-element-admin模板项目的src/permission.js文件中,给出了路由守卫.加载动态路由的实现方案,在实现了基于不同角色加载动态路由的功能.我们只需要稍作改动,就能将基于角色加

  • 详解vue-router 动态路由下子页面多页共活的解决方案

    我们都知道 vue-router 的动态路由匹配对组件是原地复用的策略,需要我们在组件中根据不同的 $route 参数展示不同的数据,这在大部分情景下是很高效的做法,但这无疑增加了组件的复杂度,而且不同参数间切换因为是同组件复用,切换效果不加修饰的话会显得很生硬,这里放一张图片感受一下. 如果我们希望能够每个动态参数都能渲染出一个组件而不是去复用怎么办呢? 我这里提供一个简便的方案 通常动态路由我们都是用来处理详情页 const router = new VueRouter({ routes:

  • vue 实现动态路由的方法

    很多时候我们在项目的路由都是在前端配置好的 但是有的时候为了进行全面的权限控制,会需要后台给出路由表,前端再渲染.不用在前端配置. 下面主要讲一下思路 1.和后台小哥哥沟通好数据,把我们前端配置的路由表数据给他,他就能看懂了 2.拿到数据需要我们自己再处理 路由中的component后台是给不了的,这里我们只需要后台小哥哥按照我们提供的前端component路径给数据,我们循环加载就可以了 //view就是后台给的数据 return () => import(`@/view/modules/${

  • Vue Router 实现动态路由和常见问题及解决方法

    个人理解:动态路由不同于常见的静态路由,可以根据不同的「因素」而改变站点路由列表.常见的动态路由大都是用来实现:多用户权限系统不同用户展示不同导航菜单. 如何利用Vue Router 实现动态路由 Vue项目实现动态路由的方式大体可分为两种: 前端将全部路由规定好,登录时根据用户角色权限来动态展示路由: 路由存储在数据库中,前端通过接口获取当前用户对应路由列表并进行渲染: 第一种方式在很多Vue UI Admin上都实现了,可以去读一下他们的源码理解具体的实现思路,这里就不过多展开.第二种方式现

  • 修改vue源码实现动态路由缓存的方法

    动态路由 官网解读 :我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件.例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染.那么,我们可以在 vue-router 的路由路径中使用"动态路径参数"(dynamic segment) 来达到这个效果. 即如果你有一个 盘点录入单 路由,但你想通过不同的传不同的 ID 来加载 CheckInputInfo 这个组件,若采用 params 方式,这时只需要在 path 后面配置 /:taskId

  • vue实现动态路由详细

    目录 一.前端控制 1.在router.js文件(把静态路由和动态路由分别写在router.js) 2.store/permission.js(在vuex维护一个state,通过配角色来控制菜单显不显示) 3.src/permission.js 4.侧边栏的可以从vuex里面取数据来进行渲染 二.后端控制路由 1.store/permission.js,在vuex里面发送请求获取数据 2.整理一份数据结构,存到表里 3.写一个转化方法,把获取到的数据转换成router结构 主流的实现方式: 简单

  • vue router动态路由下让每个子路由都是独立组件的解决方案

    vue-router 之动态路由 vue-router官网上面是这样说的 // 带查询参数,变成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }}) 然后,我就这样写了: this.$router.push({path:'manage', query: {id: 'tasklist'}})1 结果很明显,失败了.然后我就默默的再次看了一下官网,结果发现了这句话 // 命名的路由 r

  • vue router动态路由设置参数可选问题

    在日常工作中,我们需要将匹配到的所有路由,映射到一个组件上. 如下代码想要达到的效果: 不传page和id,则映射到user默认list页面 传page和id,根据page不同,显示不同的页面 问题 使用以下代码片段是不能实现以上效果的,因为默认情况下page和id参数是必传的,如果不传参数,则会根据默认路由跳转到home页面 new Router({ routes: [ { path: '/user/:page/:id', name: 'User', component: () => impo

  • 手把手教你vue实现动态路由

    目录 1.什么是动态路由? 2.动态路由的好处 3.动态路由如何实现 总结 1.什么是动态路由? 动态路由,动态即不是写死的,是可变的.我们可以根据自己不同的需求加载不同的路由,做到不同的实现及页面的渲染.动态的路由存储可分为两种,一种是将路由存储到前端.另一种则是将路由存储到数据库.动态路由的使用一般结合角色权限控制一起使用. 总结: 1)路由可变,不是写死的,动态加载 2)存储分两种:存前端,存数据库 2.动态路由的好处 使用动态路由可以跟灵活,无需手工维护,我们可以使用一个页面对路由进行维

  • GateWay路由规则与动态路由详细介绍

    目录 1.路由规则 2.动态路由 1.路由规则 Spring Cloud GateWay 帮我们内置了很多 Predicates功能,实现了各种路由匹配规则(通过 Header.请求参数等作为条件)匹配到对应的路由. 时间点后匹配 spring:    cloud:        gateway:            routes:                - id: after_route                    uri: https://example.org     

  • Vue实现动态路由导航的示例

    目录 1.导航守卫 二.功能展示 三.原理 四.功能实现 小结 1.导航守卫 “导航” 表示路由正在发生改变 正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航.有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的. 记住参数或查询的改变并不会触发进入/离开的导航守卫.你可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫. v-router官网:https://router.vuejs.org

  • vue router 动态路由清除方式

    目录 router 动态路由清除 vue-router退出登录清空路由 router 动态路由清除 重置matcher可达到路由还原效果 在用户退出时调用 resetRouter(router) 即可还原路由 import Vue from 'vue' import Router from 'vue-router' import { constantRouterMap } from '...' //导入初始化router   // 传入当前router export function reset

  • 五步教你用Nginx部署Vue项目及解决动态路由刷新404问题

    目录 步骤一:改端口 步骤二: 打包 步骤三:将dist文件夹上传到服务器上 步骤四:修改nginx.conf(重中之重) 步骤五:重启nginx 总结 期末月前本来部署过一次,昨天部署的时候发现又给忘了,而且出了很多问题,在这统一汇总一下. 步骤一:改端口 在vue.config.js下的devServer把host改成服务器的地址 步骤二: 打包 用npm run build打包,最后是这样的,并且目录下多了个dist文件夹 步骤三:将dist文件夹上传到服务器上 我用的xshell,没办法

随机推荐