vue项目 动态路由怎么做

vue项目实现动态路由的方式大体可分为两种:

1.简单的角色路由设置:比如只涉及到管理员和普通用户的权限。通常直接在前端进行简单的角色权限设置

前端这边把路由写好,登录的时候根据用户的角色权限来动态展示路由,(前端控制路由)
详情可参阅花裤衩大佬的项目手把手...

2.复杂的路由权限设置:比如OA系统、多种角色的权限配置。通常需要后端返回路由列表,前端渲染使用

后台传来当前用户对应权限的路由表,前端通过调接口拿到后处理(后端处理路由)

这两种方法各有优点,效果都能实现,我们公司是通过第二中种方法实现的,原因就是公司项目里有一个专门的用户中心,里边逻辑很复杂,不好返给前端用户权限,担心路由放到前端不安全(以上的话是公司的后台同学讲的),那好吧,抱着都试试、锻炼下自己能力的态度,我们搞了第二种方法。

Vue 动态路由的实现(后台传递路由,前端拿到并生成侧边栏)

思路整理

大体步骤:拦截路由->后台取到路由->保存路由到localStorage(用户登录进来只会从后台取一次,其余都从本地取,所以用户,只有退出在登录路由才会更新)

后端管理创建菜单需要

菜单名字menName;
菜单路径menPath;
菜单指向的资源menuUrl(也就是组件地址, 一般从views层级开始写)

前端登录后通过接口请求拿到菜单数据后,

将菜单数据转换格式 转成前端需要的路由表

menName ---> name
menPath ---> path
menuUrl ---->components文件

转换时,用到这个方法找组件资源 把 menuUrl 可以变为components的格式,转为组件文件

module.exports = file => require('@/views' + file + '.vue').default // vue-loader at least v13.0.0+

生成路由表

可以再过滤一遍生成的路由表
下面这个方法找到views底下所有的组件资源

require.context('@/views', true, /\.vue$/).keys().forEach(v => map.set(v.replace(/^\.(.*)\.vue$/,'$1'), true))

export default map

路由表里路由的组件在所有组件资源里没找到时,将该路由的path变为/404

过滤后,再动态添加

getRouter.push({ path: '*', redirect: '/404', hidden: true });
router.addRoutes(getRouter); //动态添加路由

代码

前置工作:配置项目路由文件,该文件中没有路由,或者存在一部分公共路由,即没有权限的路由

import Vue from 'vue'
import Router from 'vue-router'
import Layout from '@/layout';
Vue.use(Router)
// 配置项目中没有涉及权限的公共路由
export const constantRoutes = [
    {
        path: '/login',
        component: () => import('@/views/login'),
        hidden: true
    },
    {
        path: '/404',
        component: () => import('@/views/404'),
        hidden: true
    },
]

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

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

export default router

1.登录后通过接口拿到后端返回的路由数据

每个路由都使用到组件Layout,这个组件是整体的页面布局:左侧菜单列,右侧页面,所以children下边的第一级路由就是你自己的开发的页面,meta里包含着路由的名字,以及路由对应的icon;
因为可能会有多级菜单,所以会出现children下边嵌套children的情况;
路由是数组格式

后端返回的路由格式
"data": {
    "router": [
      {
        "path": "",
        "component": "Layout",
        "redirect": "dashboard",
        "children": [
          {
            "path": "dashboard",
            "component": "dashboard/index",  
            "meta": {
              "title": "首页",
              "icon": "dashboard"
            }
          }
        ]
      },
      {
        "path": "/example",
        "component": "Layout",
        "redirect": "/example/table",
        "name": "Example",
        "meta": {
          "title": "案例",
          "icon": "example"
        },
        "children": [
          {
            "path": "table",
            "name": "Table",
            "component": "table/index",
            "meta": {
              "title": "表格",
              "icon": "table"
            }
          },
          {
            "path": "tree",
            "name": "Tree",
            "component": "tree/index",
            "meta": {
              "title": "树形菜单",
              "icon": "tree"
            }
          }
        ]
      },
      {
        "path": "/form",
        "component": "Layout",
        "children": [
          {
            "path": "index",
            "name": "Form",
            "component": "form/index",
            "meta": {
              "title": "表单",
              "icon": "form"
            }
          }
        ]
      },
      {
        "path": "*",
        "redirect": "/404",
        "hidden": true
      }
    ]
  }

实际前端需要的 component是 component: () => import('@/views/content/classify'),

2.将后端传回的"component": "Layout", 转为 "component": Layout组件对象

因为有多级路由的出现,所以要写成遍历递归方法,确保把每个component转成组件对象
因为后台传回的是字符串,所以要把加载组件的过程 封装成一个方法,用这个方法在遍历中使用;详情查看项目里的router文件夹下的 _import_development.js和_import_production.js文件
Layout我放的目录跟其他文件的目录不一样,所以我在遍历里单独处理,各位小伙伴可自己调整哈

router 文件夹下的 _import_development.js
module.exports = file => require('@/views' + file + '.vue').default // vue-loader at least v13.0.0+
filterAsyncRouter方法 将后端返回的路由改为加载组件的过程
const _import = require('./router/_import_' + process.env.NODE_ENV) // 获取组件的方法

import LayoutPage from '@/views/layout'  // Layout 是架构组件,不在后台返回,在文件里单独引入

function filterAsyncRouter(asyncRouterMap) { // 遍历后台传来的路由字符串,转换为组件对象
  const accessedRouters = asyncRouterMap.filter(route => {
    if (route.component) {
      if (route.component === 'Layout') { // Layout组件特殊处理
        route.component = LayoutPage 
      } else {
        route.component = _import(route.component)
      }
    }
    if (route.children && route.children.length) {
      route.children = filterAsyncRouter(route.children)
    }
    return true
  })

  return accessedRouters
}

3.使用beforeEach、addRoutes、localStorage来配合实现拿到数据再转换成路由

beforeEach路由拦截,进入判断,如果发现本地没有路由数据,那就利用axios后台取一次,取完以后,利用localStorage存储起来,利用addRoutes动态添加路由,

ps:beforeEach好坏啊,一步小心就进入到了他的死循环,浏览器都tm崩了,得在一开始就加判断,拿到路由了,就直接next(),嘤嘤嘤
global.antRouter是为了传递数据给左侧菜单组件进行渲染

import axios from 'axios'

var getRouter   //  用来获取后台拿到的路由

router.beforeEach((to, from, next) => {
  if (!getRouter) {  // 不加这个判断,路由会陷入死循环 (如果没有路由)
    if (!getObjArr('router')) {  // 缓存里没有路由,axios重新获取
      axios.get('https://www.easy-mock.com/mock/5a5da330d9b48c260cb42ca8/example/antrouter').then(res => {
        getRouter = res.data.data.router//后台拿到路由
        saveObjArr('router', getRouter) //存储路由到localStorage

        routerGo(to, next) //  添加路由  执行路由跳转方法
      })
    } else { //  从localStorage拿到了路由
      getRouter = getObjArr('router')//拿到路由
      routerGo(to, next)  //  //  添加路由  执行路由跳转方法
    }
  } else {
    next()
  }

})

// routerGo
function routerGo(to, next) {
 // 这里后端返回的数据getRouter  可能不是我们需要的结构,那么需要先有一步转换的步骤
// 直到转换成前面第一步那种vue标准路由表的格式
  getRouter = filterAsyncRouter(getRouter)  // 过滤路由  转换路由的component   
  // filterAsyncRoute方法是把后端返回的路由数据 转换为前端要的路由结构
  getRouter.push({ path: '*', redirect: '/404', hidden: true });
  router.addRoutes(getRouter) //动态添加路由
  global.antRouter = getRouter //将路由数据传递给全局变量,做侧边栏菜单渲染工作
  next({ ...to, replace: true })
}

function saveObjArr(name, data) { //localStorage 存储数组对象的方法
  localStorage.setItem(name, JSON.stringify(data))
}

function getObjArr(name) { //localStorage 获取数组对象的方法
  return JSON.parse(window.localStorage.getItem(name));

}

4.拿到遍历好的路由,进行左侧菜单渲染

上边第三步会给 global.antRouter赋值,这是一个全局变量(可以用vuex替代),菜单那边拿到路由,进行渲染

你可能感兴趣的:(vue项目 动态路由怎么做)