Vue3功能实战 —— 动态路由、路由签权、动态组件 使用示例

9a69fede8b2044a79dd834e3e48f20b4.png前期回顾f8e3cc1a0f694ac2b665ca2ad14c49d7.png 

Vue3 + TS + Vite —— 大屏可视化 项目实战_vue3可视化大屏_彩色之外的博客-CSDN博客大屏可视化项目实战_vue3可视化大屏https://blog.csdn.net/m0_57904695/article/details/131014666?spm=1001.2014.3001.5501

目录

👍  动态组件

 👀 动态路由

🌍 项目地址:


👍  动态组件

顾名思义:动态显示指定的组件,动态组件不需要响应式。参考vue3动态组件的使用 - 掘金 (juejin.cn)

效果图:

 

代码示例: 

<script lang="ts" setup>
import { defineAsyncComponent, ref } from 'vue';

// 需要加载的组件集合
const components = new Map<string, any>();

// 默认加载的组件名
const compName = 'MyTag';
// 引入组件
components.value.set(
	'MyTag',
	defineAsyncComponent(() => import('./a.vue'))
);
components.value.set(
	'Item',
	defineAsyncComponent(() => import('./b.vue'))
);


// 组件切换
function onClick() {
	if (compName.value === 'MyTag') {
		compName.value = 'Item';
	} else {
		compName.value = 'MyTag';
	}
}
</script>

<template>
	<div>
		<el-button @click="onClick">改变组件</el-button>
		<component :is="components.get(compName)"></component>
	</div>
</template>


 👀 动态路由

动态路由分为两种:
1:前端路由 - 使用 import.meta.glob 获取项目文件结构动态生成路由配置。如果是嵌套菜单可以利用微信小程序的写法,在文件下新建meatPage.ts,用来配置meat信息

2:后端接口,使用生成 addRoute 将响应数据做递归放入路由示例中

前端路由:

        更新中…… 

后端路由:

index.ts 静态路由,不需要权限的

/*   静态路由 :不需要权限的路由
 *  createWebHistory 与 createWebHashHistory 的区别
 *    createWebHistory:使用 HTML5 History API 的路由模式。注意:这种模式要玩好,还需要后台配置支持。后台不配置你本地开发没问题,
 *    一旦部署上线,刷新就会出现 404。
 *    createWebHashHistory:使用 URL hash 值来作路由。支持所有浏览器,包括不支持 HTML5 History API 的浏览器。
 *
 *    详细来说,createWebHistory 是基于 HTML5 History API 的,而 createWebHashHistory 是基于 URL 的 hash 值的。
 *    在 Vue3 中,你可以通过调用 createWebHistory 或 createWebHashHistory 函数来创建路由。
 *
 *  createWebHistory 监听浏览器的 history.pushState 和 history.replaceState 事件,并使用 HTML5 的 history API 来管理路由。
 *  拥有更简单的 URL,不包含 "#" 符号。
 *
 *  createWebHashHistory 使用浏览器的 window.location.hash 属性来管理路由。在 URL 中将使用 "#" 符号,例如:`http://localhost:300/#/about`。
 *  变化时无需向服务器发送请求,对于只需要处理前端路由的应用程序来说,使用 Hash 模式足以满足需求。Hash 模式在传输数据量方面更小,而且兼容性最好。
 *
 *  在选择使用哪种模式之前,你应该考虑以下因素:
 *
 *  - **历史访问记录管理**:createWebHistory 可以管理浏览历史记录,使浏览器的后退/前进按钮可用,而 createWebHashHistory 不支持这些功能。
 *  - **URL 文本可读性**:createWebHistory 生成的 URL 更具可读性,不包含任何无用信息,通常比 createWebHashHistory 生成的 URL 更优。
 *  - **部署环境**:如果你的应用程序必须在较旧的浏览器上运行(如 IE 11 等),则应使用 createWebHashHistory。
 *  由于旧版浏览器不支持 HTML5 history API,使用 createWebHistory 可能会导致问题。
 *  - **服务器配置**:在使用 createWebHistory 时需要确保你的服务器(例如,Apache 或 Nginx)已正确配置,以避免服务端路由失败的问题。
 *  createWebHashHistory 不需要服务器配置,因为 URL 中的哈希符号是在客户端处理的,不会向服务器发送任何请求。
 *
 *  因此,如果你的应用程序仅使用前端路由,无需后退/前进按钮,或者你专注于支持现代浏览器,则应使用 createWebHistory。
 *  否则,如果应用程序部署在旧的浏览器上,则应使用 createWebHashHistory。
 */

/*
  RouteRecordRaw是Vue Router的一个类型定义,它用于描述路由配置的对象。它包含以下属性:
  path:字符串,表示路由的路径。
  name:字符串,表示路由的名称。
  component:组件类型,表示路由所匹配的组件。
  children:子路由配置数组,用于描述嵌套路由。
  meta:对象,用于存储额外的路由元数据,例如需要验证用户权限的信息。
*/

import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";

// pinia路由
import pinia from "./modules/pinia-store";
// 默认静态路由,不需要权限的路由
export const routes: Array<RouteRecordRaw> = [
  {
    path: "/",
    redirect: "/home",
  },
  {
    path: "/home",
    name: "home",
    meta: {
      loading: true,
    },
    component: () => import("@/views/home-page/home-page.vue"),
  },
  {
    path: "/about",
    name: "about",
    meta: {
      loading: true,
    },
    component: () => import("@/views/about-page/about-page.vue"),
  },
  pinia,
  {
    // vue-router4动态加载的模式下,当我们在当前页面刷新浏览器时,会出现一个警告
    // [Vue Router warn]: No match found for location with path
    // 解决方法: 在路由配置中添加一个通配符的路由,用来匹配所有的路由地址 404
    // 如果url找不到就会报404,必须放在路由页面最下面
    path: "/:catchAll(.*)",
    component: () => import("@/views/errors-view/not-found.vue"),
  },
];

export const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

动态路由、路由拦截

// 路由守卫 用来动态生成路由
import { router, routes } from "./index";
import { getLocalKey } from "@/utils/storage";
//引入main.ts中的app
import app from "../main";
const hideLoading = () => app.config.globalProperties.$Loading.hideLoading();
const showLoading = () => app.config.globalProperties.$Loading.showLoading();
import { NavigationGuardNext, RouteLocationNormalized } from "vue-router";

let isRoutesGenerated = false; // 添加一个标志位,用来判断是否已经生成了动态路由

// 前置守卫
router.beforeEach((to, from, next) => {
	if (to.meta.loading) showLoading();

	/**
	 *  token 是登录成功得到的。如果用户本地模拟token,也会调用接口,如果token过期或者被非法篡改,会在axios的拦截器中进行处理。
	 */
	if (to.path === "/login") {
		next();
		return;
	}
	if (getLocalKey("token")) {
		addRouters(next, to);
	} else {
		// 没token不是权限页面
		if (!to.meta.isRelease) {
			addRouters(next, to);
		} else {
			next({
				path: "/login",
				replace: true,
			});
		}
	}
});

// 调用接口获取数据,动态生成路由
function addRouters(next: NavigationGuardNext, to: RouteLocationNormalized) {
	//先执行的是 isRoutesGenerated,默认false,然后再取反。第一次进来是true,所以会执行里面的代码
	if (!isRoutesGenerated) {
		// 判断是否已经生成了动态路由
		try {
			// 从后台获取菜单 axios.get('/api/menu')
			const menu: RouteItem[] = [
				{
					path: "/test1",
					name: "test1",
					meta: {
						loading: true,
						keepAlive: true,
					},
					component: () => import("@/views/dynamic-routing/index-test1.vue"),
				},
				{
					path: "/test2",
					name: "test2",
					meta: {
						loading: true,
						keepAlive: true,
						isRelease: true,
					},
					component: () => import("@/views/dynamic-routing/index-test2.vue"),
				},
				{
					path: "/test3",
					name: "test3",
					meta: {
						loading: true,
						keepAlive: true,
						isRelease: true,
					},
					component: () => import("@/views/dynamic-routing/index-test3.vue"),
				},
				{
					path: "/menu",
					name: "menu",
					meta: {
						loading: true,
						keepAlive: true,
						isRelease: true,
					},
					component: () => import("@/views/menu/index.vue"),
				},
			];
			//  生成动态路由
			generateRoutes(menu);
			isRoutesGenerated = true; // 设置标志位为true,表示已经生成了动态路由
			//解决动态路由刷新页面后,404  next()是放行,next({path:to.path,replace:true})是重定向
			next({
				path: to.path,
				replace: true,
			});
		} catch (error) {
			hideLoading();
			new Error(error as string);
		}
	} else {
		next();
	}
}

// 根据菜单数据动态生成路由
function generateRoutes(menu: string | any[]): void {
	for (let i = 0; i < menu.length; i++) {
		const item = menu[i];
		const {
			path,
			name,
			meta: { loading, keepAlive, isRelease },
			component,
		} = item;
		const route: RouteItem = {
			path: path,
			name: name,
			meta: {
				loading: loading,
				keepAlive: keepAlive,
				isRelease: isRelease,
			},
			component: component,
		};

		// 递归生成子路由
		if (item.children && item.children.length > 0) {
			route.children = generateRoutes(item.children);
		}

		// 追加在404页面前面
		routes.splice(routes.length - 1, 0, route);
		// 在路由中添加新路由
		router.addRoute(route);
	}
}
router.afterEach((to) => {
	if (to.meta.loading) hideLoading();
});

export default router;

封装本地存储:
 

/**
 * window.localStorage 浏览器永久缓存
 * @method set 设置永久缓存
 * @method get 获取永久缓存
 * @method remove 移除永久缓存
 * @method clear 移除全部永久缓存
 */
export const Local = {
	// 设置永久缓存
	set(key: string, val: any) {
		window.localStorage.setItem(key, JSON.stringify(val));
	},
	// 获取永久缓存
	get(key: string) {
		const json = <string>window.localStorage.getItem(key);
		// !null为true
		if (!json) return null;
		// 这里是防止 在本地直接修改了localStorage的值,不经过上面转换,导致JSON.parse报错
		return JSON.parse(JSON.stringify(json));
	},
	// 移除永久缓存
	remove(key: string) {
		window.localStorage.removeItem(key);
	},
	// 移除全部永久缓存
	clear() {
		window.localStorage.clear();
	},
};

/**
 * window.sessionStorage 浏览器临时缓存
 * @method set 设置临时缓存
 * @method get 获取临时缓存
 * @method remove 移除临时缓存
 * @method clear 移除全部临时缓存
 */
export const Session = {
	// 设置临时缓存
	set(key: string, val: any) {
		window.sessionStorage.setItem(key, JSON.stringify(val));
	},
	// 获取临时缓存
	get(key: string) {
		const json = <string>window.sessionStorage.getItem(key);
		if (!json) return null;
		return JSON.parse(JSON.stringify(json));
	},
	// 移除临时缓存
	remove(key: string) {
		window.sessionStorage.removeItem(key);
	},
	// 移除全部临时缓存
	clear() {
		window.sessionStorage.clear();
	},
};
/**
 * 获取本地的key集合
 * @method getLocalKey
 * @param { string }  key - 要获取的key值
 * @param { object }  type - 从那里获取 localStorage、sessionStorage
 * @description 传入key值,返回匹配的key,不传返回全部key数组
 * @returns { string } 返回匹配的key或者全部key数组
 * @example
 * > getLocalKey('token') // 返回local token
 * > getLocalKey('token', localStorage) // 返回local token
 * > getLocalKey('token', sessionStorage) // 返回session token
 * > getLocalKey() // 返回全部key数组
 * @author zk
 * @createDate 2023/08/17 13:58:19
 * @lastFixDate 2023/08/17 13:58:19
 */
export const getLocalKey = (
	key?: string,
	type: object = localStorage
): string[] | string | undefined => {
	const keys = Object.keys(type);
	if (!key) return keys;
	if (keys.length > 0) {
		for (let i = 0; i < keys.length; i++) {
			const item = keys[i];
			if (item.indexOf(key) > -1) {
				return item;
			}
		}
	}
	return undefined;
};

🌍 项目地址:

Vite + Ts + Vue3 - template --- 模板: 🎉🎉🔥 Vite + Vue3 + Ts + router + Vuex + axios + eslint 、prettier、stylelint、husky、gitCommit--- 集成多种组件、Hooks支持开封即用,严格的代码质量检验、祝您轻松上大分😷🤺🤺🤺 【动态路由、特效、N个组件、N个自定义指令...】

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彩色之外

你的打赏是我创作的氮气加速动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值