0


Vue实现动态路由

前言

最近在学权限相关的管理项目,前端用到了动态路由,就是根据用户角色显示不同的菜单,动态添加路由。说着好像很简单,但看了很多教程,也跟着教程写了很多代码,但最后都跑不通(很多原因,比如:版本问题,教程没有全部代码,只有核心代码片段等)。😭😭😭所以,我只能狠下心,写下这篇博客,方便自己总结,也为了避免大家踩坑。

一、项目介绍

废话不多说,直接附上项目下载地址:
gitee:https://gitee.com/wusupweilgy/springboot-vue.git

蓝奏云:https://wwp.lanzoup.com/iO74W0r6bcob

1.开发环境

前端:vue2+element-ui组件

2.功能

根据用户的账号密码,生成不同的菜单,动态添加路由

3.项目运行截图

二、实现

1.动态路由如何实现

流程:

  1. 登录的时候,根据登录用户返回此角色可以访问的页面的路由和token
  2. 前端将路由存储到sessionStorage和vuex中(因为vuex存储的数据一刷就没了,所以需要配合sessionStorage)
  3. 在路由前置守卫处动态添加拿到的路由,对页面进行渲染。

2.项目目录介绍

  • assets:存放图片和样式
  • router:路由配置 - dynamicRoutes.js:动态路由模板- index.js:静态路由- permission.js:路由前置守卫
  • store:vuex存储全局的状态变量
  • util:工具类 - index.js:动态添加路由工具类- request.js:axios请求工具类
  • views:存放页面

3.核心代码

1)/views/Login.vue:登录(onSubmit方法)成功时,根据用户名密码区分角色,根据角色将不同路由和token写入vuex,dynamicRoutes.js中有两个路由模板,分别是user和admin,然后调用generateRoutes()方法向vue-router中动态添加路由

import {admin,user} from '@/router/dynamicRoutes'
...

onSubmit(formName) {
    //为表单绑定验证功能
    this.$refs [formName].validate((valid) => {
        if (valid) {
            if(this.form.username==="user"&&this.form.password==="user"){
                store.commit('SET_MENULIST', user);
                store.commit('SET_TOKEN', 'user');
            }else if(this.form.username==="admin"&&this.form.password==="admin"){
                store.commit('SET_MENULIST', admin);
                store.commit('SET_TOKEN', 'admin');
            }else{
                this.$message.error("登录失败")
                return
            }
            this.$message.success("登录成功")
            generateRoutes()
            this.$router.replace('/')

        } else {
            this.dialogVisible = true;
            return false;
        }
    });
},

2)/store/index.js:vuex状态管理(配合sessionStorage)

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    token:'',
    menuList:[],
    isLoadRouters:false
  },
  getters: {
    GET_TOKEN:state => {
      state.token = sessionStorage.getItem("token")
      return state.token
    },
    GET_MENULIST:state => {
      state.menuList = JSON.parse(sessionStorage.getItem("menuList")||'[]')
      return state.menuList
    },
    GET_ISLOADROUTERS:state=>{
      return state.isLoadRouters
    }
  },
  mutations: {
    SET_TOKEN:(state,token)=>{
      state.token = token
      sessionStorage.setItem("token",token);
    },
    SET_MENULIST:(state,menuList)=>{
      state.menuList = menuList
      sessionStorage.setItem("menuList",JSON.stringify(menuList));
    },
    SET_ISLOADROUTERS:(state,isLoadRouters)=>{
      state.isLoadRouters = isLoadRouters
    }
  },
})

3)/util/index.js:方法动态添加路由工具

import router from '../router'
import store from '@/store'

//动态添加路由
export function generateRoutes() {
    const _asyncRoutes = store.getters.GET_MENULIST
    if(_asyncRoutes==null)
        return
    _asyncRoutes.forEach(menu => {
        if (menu.children) {
            menu.children.forEach(m => {
                let route = menuToRoute(m, menu.name);
                if (route) {
                    router.addRoute("Home",route)
                }
            })
        }
    })
}

//将菜单转换成router可以识别的路由
const menuToRoute = (menu, parentName) => {
    if (!menu.component) {
        return null;
    } else {
        let route = {
            name: menu.name,
            path: menu.path,
            meta: {
                parentName: parentName
            }
        }
        route.component = () => import('@/views/' + menu.component + '.vue');
        return route;
    }
}

4)/router/permission.js:前置路由守卫,判断是否登录和是否已经添加过路由

import router from "@/router/index"
import store from "@/store"
import {generateRoutes} from "@/util";

// 检查是否存在于免登陆白名单
function inWhiteList(toPath) {
    const whiteList = ['/login', '/404']
    const path = whiteList.find((value) => {
        // 使用正则匹配
        const reg = new RegExp('^' + value)
        return reg.test(toPath)
    })
    return !!path
}

router.beforeEach((to, from, next) => {
    console.group('%c%s', 'color:blue', `${new Date().getTime()}  ${to.path} 的全局前置守卫----------`)
    console.log('所有活跃的路由记录列表', router.getRoutes())
    console.groupEnd()
    const token = store.getters.GET_TOKEN
    let isLoadRouters = store.state.isLoadRouters
    if (inWhiteList(to.path)) {
        next()
    } else {
        //用户已登录
        if (token && JSON.stringify(store.getters.GET_MENULIST) !== '[]') {
            if (isLoadRouters) {
                // console.log('路由已添加,直接跳转到目标页面');
                next()
            } else {
                //解决刷新页面空白
                //console.log('重新加载路由,并跳转到目标页');
                let menuList = store.getters.GET_MENULIST
                store.commit('SET_ISLOADROUTERS', true)
                //添加动态路由
                generateRoutes(menuList)

                next({...to, replace: true})
            }
        } else {
            // console.log('无登录信息,跳转到登录页');
            store.commit('SET_ISLOADROUTERS', false)
            next(`/login`)
        }
    }

})

4.坑和知识点

1)遇到的坑

  • 这里有个大坑,就是页面刷新的情况,页面一旦刷新,动态添加的路由也会清空,只剩静态路由,所有还要判断页面刷新的操作,这里通过一个vuex的变量解决,因为刷新操作,vuex的值也会刷新,所以我在vuex中设置了isLoadRouters:false这个变量,如果刷新,这个值就是默认的false
  • 第二个坑就是,在addRoute()之后第一次访问被添加的路由会白屏,这是因为刚刚addRoute()就立刻访问被添加的路由,然而此时addRoutes()没有执行结束,因而找不到刚刚被添加的路由导致白屏。因此需要从新访问一次路由才行,所以需要next({...to, replace: true}),来保证路由添加完了再进入页面 (可以理解为重进一次)。小伙伴可以试试把它换成next(),看会不会出现白屏。是

这两个坑就已经卡了我很长时间了,更何况还有其他小坑(哭死我了)

  • 用户点击退出后,需要重置路由,不然你添加过路由了,再通过addRoute()方法添加路由就会造成重复添加路由,浏览器的控制台会有警告,不重置也没事,就是看着有点不舒服。
// 重置路由
export function resetRouter() {
    const newRouter = createRouter()
    router.matcher = newRouter.matcher
}

2)学到的知识点

  • js语法:两个感叹号,将非布尔型变量转为布尔值
// 检查是否存在于免登陆白名单
function inWhiteList(toPath) {
    const whiteList = ['/login', '/404']
    const path = whiteList.find((value) => {
        // 使用正则匹配
        const reg = new RegExp('^' + value)
        return reg.test(toPath)
    })
    return !!path
}
  • component: () => import("@/views/Index") 实现路由组件懒加载。vue这种单页面应用单页面应用单页面应用,如果我们不去做路由懒加载,打包之后的文件将会异常的大,就会造成进入首页时,需要加载的内容过多,时间过长,会出现长时间的白屏,不利于用户体验,运用懒加载就可以将页面进行划分,需要的时候加载页面,可以有效的分担首页所承担的加载压力,减少首页加载用时。
  • next({...to, replace: true}) 重新访问一次路由,避免路由还没添加完成就访问路
  • router.matcher = newRouter.matcher相当于重置路由,避免重复添加路由

小结

本文介绍了使用Vue实现动态路由。关于这个东西,我网上找的很多教程都不怎么靠谱,给我挖了很多坑,所以希望我的这篇教程可以帮助你,少走点弯路,快速掌握这技术,代码的下载地址在文章开头,记得npm install一下,就可以运行了。如果这篇文章有幸帮助到你,希望读者大大们可以给作者给个三连呀😶‍🌫️😶‍🌫️😶‍🌫️


本文转载自: https://blog.csdn.net/weixin_51603038/article/details/129721990
版权归原作者 无所谓^_^ 所有, 如有侵权,请联系我们删除。

“Vue实现动态路由”的评论:

还没有评论