vue3 动态路由及使用动态路由后刷新界面出现空白页或者404

最近编写vue3动态路由的功能遇到了一些问题,处理好了,总结出来,希望能帮助到你。
  • 正片开始
    先写好本地缓存菜单的方法(存储、删除、获取)
// utils/menu.js

const getMenuList = () => {
	return JSON.parse(localStorage.getItem('menu_list'));
};

const setMenuList = (menuList) => {
	localStorage.setItem('menu_list', JSON.stringify(menuList));
};

const clearMenuList = () => {
	localStorage.removeItem('menu_list');
};

export { getMenuList, setMenuList, clearMenuList };

  • pinia (vuex) 里创建一个全局变量存储菜单数组 && 在 actions方法里创建登录和退出登录的异步方法
// store/modules/user.js

import { defineStore } from "pinia";
import { setMenuList, clearMenuList, getMenuList } from "@/utils/menu";

const useUserStore = defineStore("user", {
  state: () => {
    return {
      token: undefined,
      userMenuPermission: [] // 菜单数组初始我们设为空数组,等下需要他做判断
    }
  },

  getters: {
    userInfo(state) {
      return { ...state };
    },
  },

  actions: {
    // 设置用户信息
    setInfo(partial) {
      this.$patch(partial);
    },

    // 重置用户信息
    resetInfo() {
      this.$reset();
    },

    // 获取用户信息
    info() {
        // 这可判断一下 本地存储的菜单是否存在,存在就给 userMenuPermission赋值就行不掉接口,我这没有整,你们按照自己需求来 
        getUserInfo().then((res) => {
          this.setInfo(res.data); // 将用户信息设置给state,但是注意需要key相同
          setMenuList(res.data.userMenuPermission) // 缓存菜单数组
          generateRoutes(this.userMenuPermission) // 添加动态路由
        });
    },

    // 账号密码登录
    async loginPwd(form, pubKey) {
      // ...省略
      await setToken(res.data.token); // 保存token 存储手法和菜单的一样
    },
    
    // 退出登录
    logout() {
        userLogin.logout().then((res) => {
          this.logoutCallBack();
        });
    },
    
    // 退出登录清空token和用户信息
    logoutCallBack() {
      this.resetInfo(); // 清空 state
      clearToken(); // 清除 token
      clearMenuList(); // 清除 MenuList
    },
  },
});
export default useUserStore; // 暴露模块

// store/index.js
import { createPinia } from "pinia";
import useUserStore from "./modules/user";

const pinia = createPinia();

export { useUserStore };
export default pinia;

  • 处理路由配置
// router/modules/dynamicRouter.js 处理菜单数组和动态添加路由

import { DEFAULT_LAYOUT } from "./staticRouter"; // layout (布局)
import router from "@/router/index"; 

// 1. 引入views下的所有文件 (使用vite创建的需要这样动态导入地址)
const modules = import.meta.glob('@/views/**/*.vue')

/**
 * router.addRoute(route: RouteRecord):动态添加路由
 * router.removeRoute(name: string | symbol):动态删除路由
 * router.hasRoute(name: string | symbol): 判断路由是否存在
 * router.getRoutes(): 获取路由列表
 */

// 根据菜单信息递归生成路由(记得递归一定要出口)
export function generateRoutes(menuList = []) {
  const routes = [];
  menuList.forEach((menu) => {
    let routeItem = {};
    //如果是目录
    if (menu.meta.type == 1) {
      routeItem = {
        //路由名称
        name: menu.name,
        //路由地址
        path: menu.path,
        //组件路径
        component: DEFAULT_LAYOUT, // 是目录就为布局路径
        children: [],
        meta: menu.meta
      };
    } else {
      //如果是菜单
      routeItem = {
        //路由名称
        name: menu.name,
        //路由地址
        path: menu.path,
        //组件路径
        component: modules[`/src/views${menu.component}/index.vue`], // 为菜单的地址就要根据后端返回的地址来拼接,`/src/views${menu.component}/index.vue`这段不是固定的哈要根据你的文件格式来自定义,否则在上面导出文件数组中匹配不到会返回 undefined
        meta: menu.meta,
        children: [],
      };
    }
    
    if (menu.children && menu.children.length > 0) {
      routeItem.children = generateRoutes(menu.children);
    }
    
    if (!router.hasRoute(routeItem.name)) { // 防止添加了相同的菜单
      router.addRoute('layout', routeItem)
    }

    routes.push(routeItem); // 这个是拼接好处理的路由数组
  });
  return routes; // 根据需要返回即可
}

上面可能会遇到的问题:
modules[/src/views${menu.component}/index.vue] 会匹配不到你后端返回的路径,你要检查一下拼接格式是否正确,可打印出来和 const modules = import.meta.glob(‘@/views/**/*.vue’)数组里的对比查看问题

// router/modules/staticRouter.js 放置一些静态路由资源比如404、login等页面

/**
 * 
 * layout (布局)
 */
export const DEFAULT_LAYOUT = () => import('@/layout/default-layout.vue');

/**
 * staticRouter (静态路由)
 */
export const staticRouter = [
  {
    path: '/',
    redirect: '/dashboard',
  },
  {
    path: '/login',
    name: 'login',
    component: () => import('@/views/login/index.vue'),
    meta: {
      requiresAuth: false,
    },
  },
];

export const staticMenuRouter = [
  {
    name: "dashboard",
    path: "/dashboard",
    component: DEFAULT_LAYOUT,
    redirect: "/dashboard/index",
    meta: {
      title: "首页",
      sort: 1,
      icon: "icon-dashboard",
    },
    children: [
      {
        path: "index",
        name: "index",
        component: () => import("@/views/dashboard/index.vue"),
        meta: {
          title: "工作台",
          affix: true, // 用于判断TagsView是否一直固定
          hidden: true
        },
      },
    ],
  }
]

/**
 * errorRouter (错误页面路由)
 */
export const errorRouter = [
  {
    path: "/403",
    name: "403",
    component: () => import("@/views/exception/403.vue"),
    meta: {
      title: "403页面"
    }
  },
  {
    path: "/404",
    name: "404",
    component: () => import("@/views/exception/404.vue"),
    meta: {
      title: "404页面"
    }
  },
  {
    path: "/500",
    name: "500",
    component: () => import("@/views/exception/500.vue"),
    meta: {
      title: "500页面"
    }
  },
  // 解决刷新页面,路由警告
  {
    path: "/:pathMatch(.*)*",
    component: () => import("@/views/exception/404.vue")
  }
];

上面可能会遇到的问题:
没有设置 path: “/:pathMatch(.)” 当刷新界面时,动态添加的路由会清空,地址栏的路由路径找不到就会报在这里插入图片描述不影响代码正常跑但是不好看。

// router/index.js 配置路由模式,设置路由拦截等等

// import NProgress from "@/config/nprogress";
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router';
import { staticRouter, errorRouter, staticMenuRouter } from "@/router/modules/staticRouter";
import { useUserStore } from '@/store';
import { isLogin } from '@/utils/cache/auth'; // 判断token是否存在

const title = 'xxxxxx系统'

// 白名单
let whiteList = ["login", '404'];

export function getPageTitle(pageTitle) { // 拼接每个路由页面的名称显示在浏览器
    if (pageTitle) {
        return `${pageTitle} - ${title}`
    }
    return `${title}`
}

// 设置路由模式,导入静态的路由
const router = createRouter({
    history: createWebHashHistory(),
    routes: [...staticRouter, ...errorRouter, ...staticMenuRouter],
    strict: false, // strict 为 true 时,将不会匹配带有尾部斜线的路由
    // sensitive: false, // 为 true 时,将区分大小写
    scrollBehavior: () => ({ left: 0, top: 0 }) // 设置页面滚动行为(始终滚动到顶部)
});

// 路由前置首位
router.beforeEach(async (to, from, next) => {
    const userStore = useUserStore();

    // 1.NProgress 开始
    // NProgress.start(); // 一个加载动画,需要的朋友可以去看咋使用的 npm install --save nprogress,文章最后附上NProgress 的配置

    // 2.判断是访问登陆页,有 Token 就在当前页面,没有 Token 重置路由到登陆页
    if (to.path.toLocaleLowerCase() === '/login') {
        if (isLogin()) return next(from.fullPath);
        resetRouter(); // 没有token 就重置路由
        return next(); // 放行去登录页
    }

    // 3.判断访问页面是否在路由白名单地址(静态路由)中,如果存在直接放行
    if (whiteList.includes(to.path)) return next();

    // 4.判断是否有 Token,没有重定向到 login 页面
    if (!isLogin()) return next({ path: '/login', replace: true });

    // 5.我们判断 vuex里面的设置的菜单列表是否为空,为空就重新获取列表添加路由
    if (!userStore.userMenuPermission.length) {
        await userStore.info();
        return next({ ...to, replace: true }); // 在已有的路由里自己匹配,replace: true 允许用户手贱点回退
    }

    // 8.正常访问页面
    next();

});

// 重置路由
const resetRouter = () => {
    const userStore = useUserStore();
    userStore.userMenuPermission.forEach(route => {
        const { name } = route;
        if (name && router.hasRoute(name)) router.removeRoute(name);
    });
};


// 全局后置守卫
router.afterEach((to, from) => {
    // 动态设置浏览器标题
    document.title = getPageTitle(to.meta.title)
    // NProgress.done(); // 路由跳转结束动画
})

export default router;

上面可能会遇到的问题:
刷新界面后重新获取路由动态添加路由,要是异步的
在这里插入图片描述
否则会出现 next({ …to, replace: true }) 一直匹配不到存在的路由死循环
在这里插入图片描述

  • 进度条nprogress配置
// config/nprogress.js

import NProgress from "nprogress";
import "nprogress/nprogress.css";

NProgress.configure({
  easing: "ease", // 动画方式
  speed: 500, // 递增进度条的速度
  showSpinner: true, // 是否显示加载ico
  trickleSpeed: 200, // 自动递增间隔
  minimum: 0.3 // 初始化时的最小百分比
});

export default NProgress;

  • 到这就差不多了,因该可以用了,啰嗦了点

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/449899.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言——简易版扫雷

目录 前言 ​编辑 游戏规则 游戏结构的分析 游戏的设计 使用多文件的好处有以下几点: 游戏代码实现 框架(test.c) game函数(test.c) InitBoard初始化(game.c) Print打印棋盘(g…

【物联网设备端开发】FastBee Arduino固件开发指南

目录 一、收集数据 二、打开FastBeeArduino 源码 三、修改 Config.cpp 文件 四、修改物模型数据 五、小程序配网 本文以 WeMOS D1 R1(8266WIFI 模块)固件开发为例,实现以下功能: 设备认证设备 Mqtt 交互Wifi 类设备配网 一…

vue学习笔记23-组件事件⭐

组件事件 在组件的模板表达式中,可以直接使用$emit方法触发自定义事件;触发自定义事件的目的是组件之间传递数据 好好好今天又碰到问题了,来吧来吧 测试发现其他项目都可以 正常的run ,就它不行 搜索发现新建项目并进入以后,用指…

搭建mysql主从复制(主主复制)

1:设主库允许远程连接(注意:设置账号密码必须使用的插件是mysql_native_password,其他的会连接失败) #切换到mysql这个数据库,修改user表中的host,使其可以实现远程连接 mysql>use mysql; mysql>update user se…

使用C#创建服务端Web API

前言 C# Web API 是一种基于 .NET 平台(包括但不限于.NET Framework 和 .NET Core)构建 HTTP 服务的框架,用于创建 RESTful Web 服务。REST(Representational State Transfer)是一种软件架构风格,它利用HT…

linux中查看目录文件(ls)用法:

查看目录下的文件:ls(list) 作用 查看目录下的内容 格式 ls -参数 操作对象参数 参数功能-l以长格形式显示文件和目录的详细信息,ls命令默认只显示名称的短格式。-d显示指定目录本身的信息,而不显示目录下的各个文件和子目录的信息。-…

【机器学习】机器学习是什么?用在哪里?怎么用?

1.机器学习是什么? 机器学习(Machine Learning)是人工智能的一个分支,它是一种通过对数据进行训练和学习,让计算机系统从中获取知识并改善性能的方法。简而言之,机器学习使计算机具有从数据中学习并自动改…

信息系统项目管理师009:消费互联网(1信息化发展—1.3现代化创新发展—1.3.3 消费互联网)

文章目录 1.3.3 消费互联网1.基本属性2.应用新格局 1.3.3 消费互联网 消费互联网是以个人为用户,以日常生活为应用场景的应用形式,满足消费者在互联网中的消费需求而生的互联网类型。消费互联网以消费者为服务中心,针对个人用户提升消费过程的…

机器学习模型—支持向量机 (SVM)

机器学习模型—支持向量机 (SVM) 支持向量机 (SVM) 是一种强大的机器学习算法,用于线性或非线性分类、回归,甚至异常值检测任务。SVM 可用于各种任务,例如文本分类、图像分类、垃圾邮件检测、笔迹识别、基因表达分析、人脸检测和异常检测。SVM 在各种应用中具有适应性和高效…

Github上哪些好用的工具

专注于web漏洞挖掘、内网渗透、免杀和代码审计,感谢各位师傅的关注!网安之路漫长,与君共勉! Qexo-爱写博客的师傅强烈推荐 漂亮的 Hexo 静态博客编辑器。该项目是基于 Django 的 Hexo 静态博客管理后台,支持文章管理、…

【Linux杂货铺】操作系统

目录 🌈前言🌈 📁 冯诺依曼体系结构 📂 拓展问题:程序为什么要被加载到内存? 📂 主机与主机的交互 📁 操作系统的概念 📂 作用 📂 理解“管理” &#x…

Hadoop学习3:问题解决

文章目录 问题解决1. ERROR: but there is no HDFS_NAMENODE_USER defined2. JAVA_HOME is not set and could not be found.3. Hadoop-DFS页面访问不了4. namenode格式化失败,或者dfs页面打开失败5. ERROR: but there is no YARN_RESOURCEMANAGER_USER defined. Ab…

YOLOv5独家改进:backbone改进 | 最新大卷积核CNN架构UniRepLKNet,ImageNet 88% | CVPR2024

💡💡💡本文独家改进:大核卷积一统多种模态!RepLK正统续作UniRepLKNet,代替YOLOv5 Backbone 改进结构图如下: 收录 YOLOv5原创自研 https://blog.csdn.net/m0_63774211/category_12511931.html 💡💡💡全网独家首发创新(原创),适合paper !!! 💡…

对于双列集合map的学习

双列集合 特点 1.双列集合一次需要存一对数据,分别是键和值。 2.键和值一一对应,键不能重复,值能重复。 3.键值这个整体我们称之为键值对或者键值对对象,在java中叫做Entry对象。 Map的常见API Map是双列集合的顶层接口&…

ChatGLM:基于ChatGLM-6B使用ptuning进行微调,实现类instruction的效果

由于业务需要,调研下怎么训练一个虚拟角色出来,所以找了一些文档参考,其中有一个基于ChatGLM-6B使用ptuning进行微调,实现类instruction的效果的现成的项目,给大家分享下。 一、介绍 由于ChatGLM-6B 不支持instructio…

五子棋小游戏(sut实验报告)

实验目的 实现人与人或人与电脑进行五子棋对弈 实验内容 启动游戏,显示游戏参数设置界面,用户输入参数后进入游戏界面,显示棋盘及双方博弈过程,游戏过程中可选择退出游戏。判定一方获胜后结束本局游戏,可选择继续下…

Qt/QML编程之路:基于QWidget编程及各种2D/3D/PIC绘制的示例(45)

关于使用GWidget,这里有一个示例,看了之后很多图形绘制,控件使用,及最基本的QWidget编程都比较清楚了。ui的绘制: 运行后的界面如 工程中有非常丰富的关于各种图形的绘制,比如上图中circle,还有image。有下面一段readme的说明: # EasyQPainter Various operation pra…

容量治理三板斧:扩容、限流与降级

前言 随着现代软件系统日益复杂和用户规模的不断增长,分布式架构成为了保持系统高可用性与高性能的标准解决方案。然而,随之而来的是对系统容量治理的新挑战。在这样的背景下,容量治理成为了分布式系统设计和运维中不可或缺的一环。要确保系…

Java毕业设计 基于SpringBoot vue学科竞赛项目管理系统

Java毕业设计 基于SpringBoot vue学科竞赛项目管理系统 SpringBoot vue 学科竞赛项目管理系统 功能介绍 学生:登录 验证码 首页推广 图片轮播 竞赛通知 我的比赛队伍 组队招募 获奖通告 系统公告 统计分析 修改密码 修改个人信息 投诉建议 教师:登录 …

腾讯云和阿里云4核8G云服务器多少钱一年和1个月费用对比

4核8G云服务器多少钱一年?阿里云ECS服务器u1价格955.58元一年,腾讯云轻量4核8G12M带宽价格是646元15个月,阿腾云atengyun.com整理4核8G云服务器价格表,包括一年费用和1个月收费明细: 云服务器4核8G配置收费价格 阿里…