Vue项目中配置动态/静态路由及权限页面级动态生成左侧菜单

1,118 阅读4分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路

1. 目的

删除前端项目多余页面以及路由配置定义的路由定义

2. 背景

在项目中,我们要实现的最终效果是,不同用户登录进来显示出的是不同的菜单

3. 理解

(1)静态路由表

不需要做权限控制的路由,每个用户都可以访问

(2)动态路由表

需要做权限控制的路由,用户如果权限不一致,访问到的路由也不一样

4. 工作原理

用户登录之后,根据其携带的权限不同动态生成路由配置

5. 拆分静态和动态路由表

进入router/index.js文件

1.补充一个动态路由表asyncRoutes

2.将动态和静态合并在一起

注意:静态路由我们已经具备,接下来只需要生成动态路由,为了方便做权限控制,我们可以先将动态路由全部显示出来,后面我们会进行权限控制

// 静态路由表,项目中每个用户都可以访问
export const constantRoutes = [
    //省略
]

// 动态路由表,项目中不同的用户可以访问不同的功能
export const asyncRoutes = [
    //暂略
]

const createRouter = () => new Router({
  // 控制路由滚动行为,滚动到顶部
  scrollBehavior: () => ({ y: 0 }),
  // 组合到一起组成路由表
  routes: [...constantRoutes, ...asyncRoutes]
})

6. 设置路由

补充路由配置

export const asyncRoutes =[ {
    path: '命名', 
    name: '命名',
    component: () => import(相对路径),
    meta: {数据}
}
]

在router文件中将它们设置为layout组件的二级路由

 {
  path: '在@/layout的路径',
  component: Layout(组件),
  children: [{
    path: '',  // 这里当二级路由的path什么都不写的时候表示当前路由为默认路由直接渲染对应组件
    name: '命名',//给路由规则命名
    component: () => import('相对路径'),
    meta: { 数据 }
  }]
}

小结 要想把所有功能都包含在layout中,需要将他们设置为layout的子路由

7. 后续问题

随着我们开发的项目越来越大,功能越来越复杂时,我们需要动态管理的路由数量也会越多,那我们的router/index.js文件会变得越大,不利于维护

目标

router.js路由中的动态路由进行物理目录拆分,实现模块化管理

思路

将每一个路由配置在单独的文件中,然后统一导入使用

8. 创建文件

views目录下执行指令

mkdir xxx xxx xxx xxx

此指令为Linux操作系统下的,Window用不了,此时我们在git下执行即可

9. 文件内容

import Layout from '@/layout'
export default {
  path: '@/layout下的路径',
  component: Layout(组件),
  children: [{
    path: '', // 这里当二级路由的path什么都不写的时候表示当前路由为默认路由直接渲染对应组件
    name: '命名',//给路由规则命名
    component: () => import('相对路径'),
    meta: { 路由原信息 }//存储数据的对象
  }]
}

10. router/index.js

/* Layout */
import Layout from '@/layout'
import Router from './modules/xxx'
import Router from './modules/xxx'
import Router from './modules/xxx'
import Router from './modules/xxx'
// 动态路由表,项目中不同的用户可以访问不同的功能
export const asyncRoutes = [
Router,
Router,
Router,
Router,
]

const createRouter = () => new Router({
  // 控制路由滚动行为,如果出现滚动切换就让页面返回顶部
  scrollBehavior: () => ({ y: 0 }),
  // 临时组合到一起组成路由表
  routes: [...constantRoutes, ...asyncRoutes]//下面筛选权限要将...asyncRoutes省略
})

小结: 将路由拆分到不同文件中,这样可以使让我们更快地“对症下药”

11. 路由表数据处理

1.this.$router.options.routes 可以拿到完整的路由表数据

12. 权限应用--动态生成左侧菜单

分析

(1)登录成功(页面跳转),进入导航守卫:

(1)1 获取个人权限信息(使用员工账号登录,查看个人信息接口)

(2)2 生成可以访问的动态路由(之前掘金有写这里就不细说了)

image.png

router/index.js

const createRouter = () => new Router({
  // mode: 'history', // require service support,去#
  scrollBehavior: () => ({ y: 0 }),
  // routes: [...constantRoutes, ...asyncRoutes]
  routes: [...constantRoutes]
})

permission.js

    // 表示之前没有获取用户信息==第一次登录进来==需要获取用户信息
        // 获取用户信息
        // await store.dispatch('user/getInfo')
        const {
          data: {
            roles: { menus }
          }
        } = await store.dispatch('user/getInfo')
        // 深层解构menus
        // console.log(menus, asyncRoutes, 7555)
        /* 1-根据获取的权限筛选出对应的路由匹配规则 */
        var filterRouter = asyncRoutes.filter((item) => {
          // 筛选不需要的动态路径
          var name = item.children[0].name
          // 获取子路径名称
          var flag = menus.includes(name)
          // 判断权限中是否包含该路径名
          if (flag) {
            return item
          }
        })

        /* 2-动态添加路由匹配规则 */
        router.addRoutes(filterRouter)
        /* 3-将动态路由传入store中 */
        store.commit('menu/serRoutes', filterRouter)
      }

(2)在vuex中保存菜单数据

image.png

(2)1.创建store/module/menu.js用于存储路由

import { constantRoutes } from '@/router'
export default {
  state() {
    return {
      routes: []
    }
  },
  mutations: {
    serRoutes(state, route) {
      state.routes = [...constantRoutes, ...route]
    }
  },
  actions: {},
  getters: {},
  namespaced: true
}

(2)2 在store/index.js中挂载menu.js

import Vue from 'vue'
import Vuex from 'vuex'
import getters from './getters'
import app from './modules/app'
import settings from './modules/settings'
import user from './modules/user'
+ import menu from './modules/menu'

Vue.use(Vuex)

const store = new Vuex.Store({
  modules: {
    app,
    settings,
    user,
 +   menu
  },
  getters
})

export default store

(2)3 在左侧菜单栏显示文件src\layout\components\Sidebar\index.vue中进行渲染

        <sidebar-item
     -   v-for="route in routes"
     +   v-for="route in $store.state.menu.routes"
          :key="route.path"
          :item="route"
          :base-path="route.path"
        />

效果如下

image.png

总结:使用权限数据做过滤处理

image.png

bug处理

   刷新页面时的`bug`修复

问题

(1)如果我们刷新浏览器,会发现跳到了404页面

(2)对于addRoute添加的路由,再刷新时会白屏

原因

刷新浏览器,会发现跳到了`404`页面

现在我们的路由设置中的`404`页处在中间位置而不是所有路由的末尾了。

解决

(1)404bug:

注意:记得在router/index文件中删除404

permisson.js


router.beforeEach(async(to, from, next) => {
   ... ...
        filterRouter.push({ path: '*', redirect: '/404', hidden: true })

(2)解决刷新出现的白屏bug:动态路由没有及时获取

permisson.js

router.beforeEach(async(to, from, next) => {
   ... ...
           // 白屏 因为路径是后加进去的,刷新会返回到最初的页面才白屏,
        // 所以需要重定向到后加路径后的当前页面
        // path: to.path可写成to path,replace: true可省略
        //可写成 next(to.path)
        next({ path: to.path, replace: true })
        

退出登录时重置路由

问题

退出后,再次登陆,发现菜单异常 (控制台有输出说路由重复);

原因

路由设置是通过router.addRoutes(filterRoutes)来添加的,退出时,并没有清空,再次登陆,又加了一次,所以有重复。

思路

需要将路由权限重置 (恢复默认) 将来登录后再次追加才可以,不然的话,就会重复添加

router中记录两次公共路由,第一次用于正常操作,第二种用于重置路由,重置路由在登录事件触发时调用

router/index.js

const router = createRouter()

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