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

news2024/11/15 13:41:51
最近编写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(.)” 当刷新界面时,动态添加的路由会清空,地址栏的路由路径找不到就会报在这里插入图片描述不影响代码正常跑但是不好看。

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

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 userStore = useUserStore();

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

    // 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 }) 一直匹配不到存在的路由死循环
在这里插入图片描述

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

掘根宝典之C++类型别名,关键字typedef,auto,decltype

类型别名 在C中,我们可以使用typedef关键字或using关键字来创建类型别名。下面是两种方式的示例: 使用typedef关键字创建类型别名: typedef int myInt; typedef float myFloat;myInt a;//等价int a; myFloat b;//等价float b; 使用using关…

4、Generator、class类、继承、Set、Map、Promise

一、生成器函数Generator 1、声明generator函数 function* 函数名() { }调用生成器函数 需要next()返回一个generator对象,对象的原型上有一个next(),调用返回对象{value:yield后面的值,done} function* fn() {console.log("我是生成器函数") } let it…

B端系统:漂亮就行。扯淡,漂亮仅占五分之一!

Hi,我是贝格前端工场,接触N多B端系统,也优化升级过N多。在这个过程中,仅仅美观是不够的,所以我拓展出来的B端系统五度评价指标,本篇着重讲易用性指标,欢迎老铁们评论点赞转发,有需求…

crossover玩不了qq游戏大厅怎么办 仍有五亿人坚持用QQ crossover玩游戏 Mac电脑玩QQ游戏

从1999年2月,QQ首个版本QICQ(OPEN-ICQ)上线。到2024年,靠着5亿月活用户,守住社交领域TOP2位置。你还记得QQ经典的铃声吗? 根据月狐数据2023年12月的统计,QQ月活跃账户数比微博和知乎加在一起还要…

安防视频监控汇聚平台EasyCVR使用RTMP推流出现异常的原因排查与解决

AI视频智能分析/视频监控管理平台EasyCVR能在复杂的网络环境中(专网、内网、局域网、广域网、公网等),支持设备通过4G、5G、WIFI、有线等方式接入,并将设备进行统一集中接入与视频汇聚管理,经平台接入的视频流能实现多…

paddle的版面分析的环境搭建及使用

一、什么是版面分析 版面分析技术,主要是对图片形式的文档进行版面分析,将文档划分为文字、标题、表格、图片以及列表5类区域,如下图所示: 二、应用场景 2.1 合同比对 2.2 文本类型划分 2.3 通用文档的还原 版面分析技术可将以…

KIF本地密钥注入验证步骤 RSA加解密 python JAVA

**验证步骤:** # 终端随机生成一对RSA key pair pem文件 # 终端把sn及公钥发过去 # KIF返回公钥加密后的IPEK及明文IPEK的KCV (加密机处理加密等操作:把sn和Base Derivation Key分散生成IPEK用加密机的Local Master Key存入加密机&#xff0c…

『运维备忘录』之 iptables 防火墙使用指南

前言 iptables 是一个配置 Linux 内核防火墙的命令行工具,它是用来设置、维护和检查Linux内核的IP包过滤规则的。本文将介绍 iptables 的基础知识和使用示例。 注意:红帽/红旗/CentOS等 7 版本以上已改为使用 firewalld 作为防火墙替换iptables。 一、基…

AIGC: 2 语音转换新纪元-Whisper技术在全球客服领域的创新运用

背景 现实世界,人跟人的沟通相当一部分是语音沟通,比如打电话,聊天中发送语音消息。 而在程序的世界,大部分以处理字符串为主。 所以,把语音转换成文字就成为了编程世界非常普遍的需求。 Whisper 是由 OpenAI 开发…

3.11_C++_day1_作业

作业要求&#xff1a; 程序代码&#xff1a; #include <iostream> #include <string.h>using namespace std;int main() {int a0,b0,c0,d0,e0;//分别记录字符串中的大写&#xff0c;小写&#xff0c;数字&#xff0c;空格&#xff0c;其他字符个数string str;cha…

AHU 汇编 实验五

实验名称&#xff1a;实验五 分支与循环程序设计 二、实验内容&#xff1a;从键盘输入一个四位的16进制数&#xff08;其中字母为大写&#xff09;&#xff0c;将其转化为二进制数提示输出。 实验过程&#xff1a; 源代码: data segmentbuff1 db Please input a number(H):$b…

03:HAL---中断

目录 一:中断 1:简历 2:AFIO 3:EXTI 4:NVIC基本结构 5:使用步骤 6:设计中断函数 二:中断的应用 A:对外式红外传感计数器 1:硬件介绍 2:计数代码 B:旋转编码计数器 1:硬件介绍 2:旋转编码器代码 C:按键控制LED D:代码总结 一:中断 1:简历 中断&#xff1a;在主程序…

UI学习 一

教程&#xff1a;Accessibility – Material Design 3 需要科学上网&#xff0c;否则图片显示不出来。设计教程没有图片说明&#xff0c;不容易理解。 优化UI方向 清晰可见的元素足够的对比度和尺寸重要性的明确等级一眼就能辨别的关键信息 传达某一事物的相对重要性 将重…

Unity项目开发必备技能——字体替换工具的使用(上)

在平常我们做unity项目的时候&#xff0c;工具类的使用对于我们来说是必不可少的组成部分&#xff0c;因为工具类可以解决实际问题或者是优化我们已经实现的功能。 当你在做项目的时&#xff0c;我们搭建完场景后&#xff0c;当前场景中你所创建的UI组件中的Text的字体&#xf…

springboot+vue3+nuxt3+ts+minio开发的dsblog3.0前后端分离博客

springbootvue3nuxt3tsminio开发的dsblog3.0前后端博客 一、技术栈 本博客系统采用了先进且成熟的技术栈&#xff0c;包括Spring Boot 3、Spring Security、Vue 3、Nuxt 3、TypeScript、Vite、MinIO、Redis、Element Plus和Markdown等。这些技术共同协作&#xff0c;确保了博…

float32 float16 bfloat16 推理训练GPU速度和内存调研

概念&#xff1a; 参考&#xff1a;Accelerating Large Language Models with Mixed-Precision Techniques - Lightning AI 3种数量类型表示的数据范围不一样&#xff0c;以float32为例其中有1个符号位&#xff0c;8位表示指数&#xff0c;23位表示尾数 标准训练推理是用的fl…

力扣大厂热门面试算法题 15-17

15. 三数之和&#xff0c;16. 最接近的三数之和&#xff0c;17. 电话号码的字母组合&#xff0c;每题做详细思路梳理&#xff0c;配套Python&Java双语代码&#xff0c; 2024.03.11 可通过leetcode所有测试用例。 目录 15. 三数之和 解题思路 完整代码 Java Python ​…

解锁App推广新姿势:Xinstall专属二维码,让推广更高效!

在移动互联网时代&#xff0c;App推广的重要性不言而喻。然而&#xff0c;推广的过程中往往伴随着各种痛点&#xff0c;如何准确追踪用户来源、如何提高安装转化率等&#xff0c;一直是广告主和开发者们关注的焦点。今天&#xff0c;我们要为大家介绍一款专业的App全渠道统计服…

Java基于SpringBoot+Vue的人事管理系统,附源码

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Linux——文件缓冲区与模拟实现stdio.h

前言 我们学习了系统层面上的文件操作&#xff0c;也明白了重定向的基本原理&#xff0c;在重定向中&#xff0c;我们使用fflush(stdout)刷新了缓冲区&#xff0c;当时我们仅仅知道重定向需要刷新缓冲区&#xff0c;但是不知道其所以然&#xff0c;今天我们来见识一下。 一、…