小白系列Vite-Vue3-TypeScript:011-登录界面搭建及动态路由配置
前面几篇文章我们介绍的都是Vite+Vue3+TypeScript项目中环境相关的配置,接下来我们开始进入系统搭建部分。本篇我们来介绍登录界面搭建及动态路由配置,大家一起撸起来......
搭建登录界面
登陆接口api
项目登陆接口是通过mockjs前端来模拟的
模拟服务接口LoginApi
首先在src/mock文件夹下新建login.ts文件,模拟两个服务接口(验证码获取+用户登录)
import { MockMethod } from 'vite-plugin-mock';export const LoginApi: Array<MockMethod> = [{url: '/api/captchaImage',method: 'get',response: () => {return {msg: 'OK',img: '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',code: 200,uuid: '37e7a189a9b14be6a5cbae80af43abaa',};},},{url: '/api/login',method: 'post',response: () => {return {msg: 'OK',code: 200,token:'eyJhbGciOiJIUzUxMiJ9.eyJsb2dpbl91c2VyX2tleSI6Ijc2YzYzNzczLWY2ZWEtNDlkMC05MDIyLTg4ZmUxNjI1NmMzNyJ9.XUeTaH-VZu_Mm0Rm1m_lST4YH1-ovX5Gg9w_Z4nA04agzxzeTdb5XxKCIhMr8pPatCKmiCql9E7afMY96oGYfQ',};},},
];LoginApi引入
修改src/mock文件夹下的index.ts文件,添加内容如下
import { LoginApi } from './login';
const mock: Array<MockMethod> = [...LoginApi];
创建登录API
在src/api文件夹下新建login.ts文件,创建获取验证码接口captchaImage和用户登录api接口login
import request from '@/utils/request';export const captchaImage = () => {return request.get('/api/captchaImage');
};
export const login = () => {return request.post('/api/login');
};登录界面Login.vue
在src/views路径下新建Login.vue文件,这个文件就是我们的登录界面文件
<template><div class="login-main"><el-form ref="ruleFormRef" :model="ruleForm" status-icon :rules="rules" label-width="120px" class="ruleForm"><el-form-item prop="user"><el-input :prefix-icon="User" v-model="ruleForm.user" clearable /></el-form-item><el-form-item prop="pass"><el-input :prefix-icon="Lock" v-model="ruleForm.pass" type="password" /></el-form-item><el-form-item prop="code"><el-input :prefix-icon="Lock" v-model="ruleForm.code" class="code-value" /><img :src="img" alt="" class="code-img"></el-form-item><el-form-item><el-button type="primary" @click="submitForm(ruleFormRef)">登录</el-button><el-button @click="resetForm(ruleFormRef)">重置</el-button></el-form-item></el-form></div>
</template><script setup lang='ts'>
// 方法引入
import { reactive, ref, onMounted } from 'vue'
import router from '@/router';
import { setLocalStorage } from "@/utils/localstorage";
import type { FormInstance } from 'element-plus'
// 组件引入
import { User, Lock } from '@element-plus/icons-vue'
// 接口引入
import { captchaImage, login } from "@/api/login";onMounted(() => {captchaImage().then(datas => {console.log(datas)img.value = 'data:image/gif;base64,' + datas.imguuid.value = datas.uuid})
})
let img = ref<any>("")
let uuid = ref<string>("")
const ruleFormRef = ref<FormInstance>()
const validateUser = (rule: any, value: any, callback: any) => {if (value === '') {callback(new Error('用户名不能为空'))} else {callback()}
}
const validatePass = (rule: any, value: any, callback: any) => {if (value === '') {callback(new Error('密码不能为空'))} else {callback()}
}
const validateCode = (rule: any, value: any, callback: any) => {if (value === '') {callback(new Error('验证码不能为空'))} else {callback()}
}
const ruleForm = reactive({user: 'wangjianlei',pass: '123456',code: '4'
})const rules = reactive({pass: [{ validator: validatePass, trigger: 'blur' }],user: [{ validator: validateUser, trigger: 'blur' }],code: [{ validator: validateCode, trigger: 'blur' }],
})const submitForm = (formEl: FormInstance | undefined) => {if (!formEl) returnformEl.validate((valid) => {if (valid) {login().then(res => {console.log(res)setLocalStorage("LH_TOKEN", res.token)router.push("/")}).catch(err => {throw new Error(err);})} else {return false}})
}const resetForm = (formEl: FormInstance | undefined) => {if (!formEl) returnformEl.resetFields()
}
</script><style lang="scss" scoped>
.login-main {display: flex;padding: 25px;.ruleForm {width: 500px;.code-value {width: 260px;}.code-img {margin-left: 10px;width: 75px;height: 30px;}}
}
</style>配置动态路由
动态路由接口
项目动态路由接口同样是通过mockjs前端来模拟的
模拟服务接口HomeApi
首先在src/mock文件夹下新建home.ts文件,模拟动态路由返回服务接口
import { MockMethod } from 'vite-plugin-mock';export const HomeApi: Array<MockMethod> = [{url: '/api/routerList',method: 'get',response: () => {const routes = [{path: '/main/PageOne',name: 'PageOne',component: 'PageOne.vue',},{path: '/main/PageTwo',name: 'PageTwo',component: 'PageTwo.vue',},{path: '/main/PageThree',name: 'PageThree',component: 'PageThree.vue',},];return {msg: 'OK',code: 200,data: routes,};},},
];这里我们可以看到共返回了三个路由“PageOne”,“PageTwo”和“PageThree”。
HomeApi引入
修改src/mock文件夹下的index.ts文件,添加内容如下
import { MockMethod } from 'vite-plugin-mock';
import { HomeApi } from './home';
import { LoginApi } from './login';const mock: Array<MockMethod> = [...LoginApi, ...HomeApi];export default mock;
创建动态路由API
在src/api文件夹下新建home.ts文件,创建获取动态路由接口GetDynamicRoutes
import request from '@/utils/request';export const GetDynamicRoutes = () => {return request.get('/api/routerList');
};新增路由界面
根据我们利用mockjs模拟的动态路由接口返回的数据,在src/views文件夹下创建modules文件夹,新建PageOne.vue,PageTwo.vue和PageThree.vue界面文件。

动态路由配置
我们的动态路由数据应由一个公共的地方进行管理,所以呢这里我选择利用vue的状态管理器pinia来实现这个功能。
首先我们在pinia的state中添加一个路由项routes(RouteRecordRaw类型的数组):
state: (): storeHome => {return {//路由表routes: [],};
},然后在action中还需要添加一个根据路由数据加载动态路由的方法(updateRoutes),方法所需的路由数据和router对象由外部传入。(这里用外部传入router是为了避免循环调用router,毕竟需要进行加载动态路由的地方基本都有个router的示例对象,而外部传入的话,只需要调用然后传入一次router就可以了)
在src/store/type/home.ts添加状态项routes类型定义:
import { RouteRecordRaw } from 'vue-router';export type storeHome = {routes: Array<RouteRecordRaw>;
};
加载路由的思路其实也很简单,首先解析咱调用接口后传入的路由数据,根据路由的数据类型生成对应的路由表,并存储到pinia中,然后直接遍历这个pinia中的路由表,使用router.addRoute()方法将路由加载进去。router.addRoute()方法支持传如两个参数,方便我们在指定位置的路由中插入children,这种情况下第一个参数是父级路由的name,第二个参数就是要添加的children路由对象。
需要我们注意的是:vite使用动态路由,在动态导入路由组件的时候,需要特别注意不能将页面路径直接作为component导入,虽然开发环境一般是能正常加载,但是打包到生产环境的时候十有八九会报错,所以我们需要添加以下代码:
//根据自己项目实际目录结构组织,注意这里的“../../”不能用“@”别名代替
let modules = import.meta.glob('../../views/modules/*.vue');然后用modules形式引入,完整动态路由的pinia代码如下:
import { defineStore } from 'pinia';
import { storeHome } from '../types/home';let modules = import.meta.glob('../../views/modules/*.vue');export const useHomeStore = defineStore('index', {state: (): storeHome => {return {//路由表routes: [],};},getters: {},actions: {updateRoutes(data: Array<any>, router: any) {this.routes = [];data.forEach((el) => {this.routes.push({path: el.path,name: el.name,component: modules[`../../views/modules/${el.component}`],});});this.routes.forEach((el) => {router.addRoute('Home', el);// router.addRoute();});},},
});
加载动态路由
上面我们已经配置好了路由接口和加载路由的方法,加载动态路由的思路也很简单,在我们的初始页面中调用路由的数据接口,在获取到数据之后调用加载的方法即可。
// 方法引入
import router from '@/router';
// 接口引入
import { GetDynamicRoutes } from "@/api/home"
// 状态管理器引入
import { useHomeStore } from "@/store/modules/home";const homeStore = useHomeStore()
onBeforeMount(() => {if (!getLocalStorage("LH_TOKEN")) {router.push("/login")} else {alert("登陆成功")GetDynamicRoutes().then(res => {homeStore.updateRoutes(res.data, router)})finish.value = true}
})为验证我们的路由是否被加载成功,我们可以在调用动态路由同时创建对应的按钮,以便我们进行路由跳转。
<el-button v-for="item in routes" :key="item.name" @click="handleClick(item.path)">{{ item.name }}</el-button>const routes = computed(() => homeStore.routes)
// 路由按钮点击事件
const handleClick = (path: string) => {router.push(path)
}完整页面代码如下:
<template><div class="home-main" v-if="finish"><div><el-button v-for="item in routes" :key="item.name" @click="handleClick(item.path)">{{ item.name }}</el-button></div><RouterView /></div></template>
<script setup lang='ts'>
// 方法引入
import { reactive, ref, onBeforeMount, onMounted } from 'vue'
import { computed } from "@vue/reactivity";
import { getLocalStorage } from '@/utils/localstorage'
import router from '@/router';
// 组件引入
import HomeHeader from './home/HomeHeader.vue';
// 接口引入
import { GetDynamicRoutes } from "@/api/home"
// 状态管理器引入
import { useHomeStore } from "@/store/modules/home";const homeStore = useHomeStore()
onBeforeMount(() => {if (!getLocalStorage("LH_TOKEN")) {router.push("/login")} else {alert("登陆成功")GetDynamicRoutes().then(res => {homeStore.updateRoutes(res.data, router)})finish.value = true}
})
const routes = computed(() => homeStore.routes)
// 路由按钮点击事件
const handleClick = (path: string) => {router.push(path)
}
let finish = ref(false)
</script>
<style lang="scss" scoped>
.home-main {}
</style>动态路由效果

配置路由守卫
其实截止上面,我们的动态路由已经加载成功了。不过仔细测试会发现,还会有一个问题bug,假如我们刷新跳转后的页面,或者直接使用动态路由的路径进行跳转,就会出现报错“no match found for location with path '/PageOne' ”。添加的动态路由失效了,页面也没有显示,这是因为我们的路由和状态管理器pinia在刷新之后都会被重置,而我们加载路由的方法是在系统初始页面被调用的,当我们直接F5刷新页面或者直接输入路由路径的时候,初始页面其实并没有被加载,也就是说我们的动态路由并没有被加载上去,自然这个动态的页面也就丢失了。
这里我们可以通过添加路由守卫的方式来解决这个问题,大致思路:假如我们的页面请求路径不是我们定义的初始路径的时候,我们就在路由守卫中要求在跳转之前先去查询状态管理器中是否存在我们的动态路由,或者该动态路由是否满足我们的初始页面跳转要求,若不满足则请求动态路由接口并加载我们的动态路由,在加载完成后再继续执行页面跳转操作。
路由守卫代码如下:
// 路由守卫
router.beforeEach((to, from, next) => {if (to.path !== '/main' && to.path !== '/') {const store = useHomeStore();if (store.routes.length < 1) {GetDynamicRoutes().then((res) => {store.updateRoutes(res.data, router);next({ path: to.path, replace: true });}).catch((_) => {next();});} else {next();}} else {next();}
});
最终效果

至此,登录界面和动态路由基本搭建就完成了。
我相信,每天学习一点点,收获成长亿点点!
相关文章:
小白系列Vite-Vue3-TypeScript:011-登录界面搭建及动态路由配置
前面几篇文章我们介绍的都是ViteVue3TypeScript项目中环境相关的配置,接下来我们开始进入系统搭建部分。本篇我们来介绍登录界面搭建及动态路由配置,大家一起撸起来......搭建登录界面登陆接口api项目登陆接口是通过mockjs前端来模拟的模拟服务接口Login…...
C语言( 缓冲区和重定向)
一.缓冲输入,无缓存输入 while((chgetchar()) ! #) putchar(ch); 这里getchar(),putchar()每次只处理一个字符(这里只是知道就好了),而我们使用while循环,当读到#字符时停止 而看到输出例子,第一行我们输入…...
编程思想、方法论和架构的类型及应用
概要编程思想是指在编写代码时所采用的基本思维方式和方法论。分类编程思想编程思想为软件开发提供了思维范式和指导思路,例如面向对象思想、函数式编程思想等,它们帮助程序员更好地抽象问题、组织代码、提高代码复用性和可维护性,包括一下几…...
【OA办公】OA流程审批大揭秘,带你看遍所有基础流程
流程审批,是所有企业的OA办公系统重要组成部分,是任何OA办公系统都不可缺少的。比起传统的纸张传阅、签批的审批模式浪费了大量的时间和成本,因此越来越多的企业采用OA这种全新的、高效的、智能的审批模式。流程审批除了这些好处,…...
《零基础入门数据结构与算法》专栏介绍
目录 前言 第一部分:重点 第二部分:题库 第三部分:测试 第四部分:实验 第五部分:试卷 总结 前言 本专栏主要分为五个部分: ① 重要知识点详解 ② 近百道练习题解析 ③ 数据结构与算法章节测试 …...
测试开发之Django实战示例 第九章 扩展商店功能
第九章 扩展商店功能在上一章里,为电商站点集成了支付功能,然后可以生成PDF发票发送给用户。在本章,我们将为商店添加优惠码功能。此外,还会学习国际化和本地化的设置和建立一个推荐商品的系统。本章涵盖如下要点:建立…...
【Spring】一文带你吃透AOP面向切面编程技术(下篇)
个人主页: 几分醉意的CSDN博客_传送门 上节我们介绍了什么是AOP、Aspectj框架的前置通知Before传送门,这篇文章将继续详解Aspectj框架的其它注解。 文章目录💖Aspectj框架介绍✨JoinPoint通知方法的参数✨后置通知AfterReturning✨环绕通知Ar…...
【java】Spring Boot --40 个 Spring Boot 常用注解(建议收藏)
本文目录一、Spring Web MVC 注解Spring Web MVC 注解RequestMappingRequestBodyGetMappingPostMappingPutMappingDeleteMappingPatchMappingControllerAdviceResponseBodyExceptionHandlerResponseStatusPathVariableRequestParamControllerRestControllerModelAttributeCross…...
《游戏学习》| 微信对话模拟生成器源码分析
简介微信对话生成器,是一款在线微信聊天对话制作的工具,它可以设置苹果或安卓状态栏,包括手机电量、手机时间等,还可以设置不同用户的角色,然后发送文字、语音、红包、转账等多种好玩的功能,可谓是一款娱乐…...
剑指 Offer 10- I. 斐波那契数列[c语言]
目录题目思路代码结果该文章只是用于记录考研复试刷题题目 力扣斐波那契数列 写一个函数,输入 n ,求斐波那契(Fibonacci)数列的第 n 项(即 F(N))。斐波那契数列的定义如下: F(0) 0, F(1) 1 …...
【C#基础】C# 数据类型总结
序号系列文章0【C#基础】初识编程语言C#1【C#基础】C# 程序通用结构2【C#基础】C# 基础语法解析文章目录前言数据类型一. 值类型(Value types)二. 引用类型(Reference types)三. 指针类型(Pointer types)结…...
再创荣誉 | Softing工业荣获CAIMRS 2023 数字化创新奖
在刚刚结束的中国工控-第二十一届“自动化及数字化”年度评选(CAIMRS 2023)中,Softing凭借edgeAggregator产品荣获“数字化创新奖”! 经层层筛选,Softing edgeAggregator边缘聚合服务器从中脱颖而出,摘得C…...
Multi Paxos
basic paxos 是用于确定且只能确定一个值,“只确定一个值有什么用?这可解决不了我面临的问题,例如每个用户都要多次保存数据.” 你心中可能有这样的疑问。 原simple paxos论文里有提到一连串个instance of paxos [4] 但没有提出 multi paxos的概念&…...
Android - dimen适配
一、分辨率对应DPIDPI名称范围值分辨率名称屏幕分辨率density密度(1dp显示多少px)ldpi120QVGA240*3200.75(120dpi/1600.75px)mdpi160(基线)HVGA320*4801(160dpi/1601px)hdpi240WVGA4…...
深度学习网络模型——RepVGG网络详解
深度学习网络模型——RepVGG网络详解0 前言1 RepVGG Block详解2 结构重参数化2.1 融合Conv2d和BN2.2 Conv2dBN融合实验(Pytorch)2.3 将1x1卷积转换成3x3卷积2.4 将BN转换成3x3卷积2.5 多分支融合2.6 结构重参数化实验(Pytorch)3 模型配置论文名称: RepVGG: Making V…...
仓库拣货标签应用案例
使用场景:富士康成都仓库 解决问题:仓库亮灯拣选, 提高作业效率和物料明晰展示仓库亮灯拣选使用场景:京东仓库 解决问题:播种墙分拣,合单拣货完成后按订单播种播种墙分拣使用场景:和尔泰智能料…...
介绍一款HCIA、HCIP、HCIE的刷题软件
华为认证考试分为三个等级,分别为工程师HCIA、高级工程师HCIP、专家HCIE,等级越高,考试难度越大。 本篇带大家详细了解华为数通题库刷题工具的详细操作步骤。 操作须知:本款刷题工具为一款刷题小程序,无需安装即可在线…...
线程池整理汇总
它山之石,可以攻玉。借鉴整理线程池相关文章,以及自身实践。 文章目录1. 线程池概述2. 线程池UML架构3. Executors创建线程的4种方法3.1 newSingleThreadExecutor3.2 newFixedThreadPool3.3 newCachedThreadPool3.4 newScheduledThreadPool小结4. 线程池…...
华为OD机试真题Python实现【最短木板长度】真题+解题思路+代码(20222023)
🔥系列专栏 华为OD机试(Python)真题目录汇总华为OD机试(JAVA)真题目录汇总华为OD机试(C++)真题目录汇总华为OD机试(JavaScript)真题目录汇总文章目录 🔥系列专栏题目输入输出示例一输入输出说明示例二输入输出说明...
VMware安装CentOS7
个人简介:云计算网络运维专业人员,了解运维知识,掌握TCP/IP协议,每天分享网络运维知识与技能。个人爱好: 编程,打篮球,计算机知识个人名言:海不辞水,故能成其大;山不辞石…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?
一、核心优势:专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发,是一款收费低廉但功能全面的Windows NAS工具,主打“无学习成本部署” 。与其他NAS软件相比,其优势在于: 无需硬件改造:将任意W…...
简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
苍穹外卖--缓存菜品
1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得,如果用户端访问量比较大,数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据,减少数据库查询操作。 缓存逻辑分析: ①每个分类下的菜品保持一份缓存数据…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
