vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > vue 动态路由

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

作者:你不讲 wood

在Vue应用中,结合前端和后端接口请求实现动态路由,可根据用户权限动态生成路由,提高安全性与灵活性,本文就来介绍一下前端+接口请求实现vue动态路由,感兴趣的可以了解一下

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

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

实现原理

优点

示例

在前导航路由钩子 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 {
    // 没有token
    if (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 动态路由

总结

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

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

到此这篇关于前端+接口请求实现vue动态路由的文章就介绍到这了,更多相关vue 动态路由内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文