当前位置: 首页 > news >正文

vue + vue-router写登陆验证的同步方法和异步方法,及页面组件的分离和后端代码

先写一个用vue cdn写一个登陆验证的小示例+后端代码

前端719.html

<div id="app"><div id="loginForm">//路由层,登陆页和后台主页<router-link to="/">Login</router-link><router-link to="/home">Home</router-link></div>//展示组件的具体页<div><router-view></router-view></div>
</div>
<script src="https://cdn.bootcdn.net/ajax/libs/vue/2.6.13/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue-router/3.4.2/vue-router.js"></script>
<script>const Login={template:`<div><h3>后台登陆</h3><form @submit="login">	//绑定login事件<div><label for="username">用户:</label><input type="text" id="username" v-model="username" required></div><div><label for="pwd">密码:</label><input type="password" id="pwd" v-model="pwd" required></div><button type="submit">登陆</button>            </form>    </div>`,data(){return{username:'',	//与上面的input进行双向绑定pwd:''}} ,methods:{//触发login事件login(){//获取到用户填写的用户名和密码let result={username:this.username,pwd:this.pwd}//连接后端APIfetch('http://localhost:3000/api/login',{method:'post',body:JSON.stringify(result),headers:{'content-type':"application/json"}}).then((response)=>{if(response.ok){return response.json(); //返回json数据}}).then(data=>{console.log(data);if(data.success){console.log('准备跳转到/home');//路由跳转到homethis.$router.push('/home');}else{alert('登陆失败:'+data.message);}}).catch(error=>{console.error('登陆错误:',error);})}}   };//组件homeconst Home={template:`<div><h3>Home page</h3><p>欢迎来到后台页面</p></div>`};//将组件添加路由中去const router=new VueRouter({mode:'hash',	//模式为#+组件页面routes:[{path:'/',component:Login},{path:'/home',component:Home,beforeEnter:validataUser}	//beforeEnter在进入页面前进行验证]});const app=new Vue({router}).$mount("#app");//验证函数function validataUser(to,from,next){fetch('http://localhost:3000/api/check-auth').then((response)=>{if(response.ok){return response.json();}}).then(data=>{console.log('验证结果:',data);if(data.isAthenticated){next();}else{next('/');}}).catch(error=>{console.log('验证错误:',error);next('/');});}
</script>

//后端代码用的nodejs的express写的login.js

const express=require('express');
const bodyParser=require('body-parser');
const cors=require('cors'); //允许跨域
const app=express();
const port=3000;app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended:true}));
app.use(cors());//判断是否已经验证过
let isAthenticated=false;app.post('/api/login',(req,res)=>{const {username,pwd}=req.body;console.log(req.body)if(username==='admin'&& pwd==='123456'){isAthenticated=true;res.json({success:true});}else{res.json({success:false,message:'登陆失败'});}
});
//判断是否已经验证过的用户接口,主要用于后台主页判断
app.get('/api/check-auth',(req,res)=>{res.json({isAthenticated:isAthenticated});
});app.listen(port,()=>{console.log('服务器监听端口已经开启:',port);
});

这样你基本上就写了一个简单的登陆验证页面了。

下面写一个组件分离的代码

先写我们的主要路由和显示页面
App.vue
//这个很简单就是把router-link to路由到相关的页面即可,然后导出为App
//相当于分解上面719.html小示例

<template><div :class="$style.container"> <div><router-link to="/">登陆</router-link><router-link to="/home">后台</router-link></div><div><router-view></router-view></div>   </div>
</template><script>
export default{name:'App'
}
</script><style module>
.container {margin: 0;padding: 0;width: 100%;height: 100vh;background: url(/img/bg.webp) no-repeat center center;background-size: cover;position: relative;z-index: 0;
}</style>

//第2步写登陆页面
这里就是把登陆模板template、javascript代码、style写在一起
login.vue

<template><div :class="$style.loginDiv">  //绑定样式类loginDiv,这种是style为模块module可以这样写,如果style为scoped则直接写class="loginDiv"<h3>登陆</h3><form @submit.prevent="login" autocomplete="off">  //绑定login事件<div :class="$style.listDiv"><label for="username">&emsp;户:</label><input type="text" id="username" name="username" v-model="username" required autocomplete="false" @blur="validata" @input="validataOnInput"> //绑定blur、input事件,blur为失焦,input为输入事件<span v-if="error.username" :class="$style.errorMessage">{{error.username}}</span></div><div :class="$style.listDiv"><label for="pwd">&emsp;码:</label><input type="password" id="pwd" name="pwd" v-model="pwd" required autocomplate="false" @blur="validata" @input="validataOnInput"><span v-if="error.pwd" :class="$style.errorMessage">{{error.pwd}}</span></div><div :class="$style.listDiv"><button type="submit" :class="$style.btn">&emsp;</button></div></form></div>
</template>
<script>export default{data(){return{username:'',pwd:'',error:{username:'',pwd:''}}},methods:{login(){//只要验证未通过则return,表示不会进行下面的fetch接交给后端if(!this.validata()){return;}let result={username:this.username,pwd:this.pwd};fetch('http://localhost:3000/api/login',{method:'post',body:JSON.stringify(result),headers:{'content-type':"application/json"}}).then((response)=>{if(response.ok){return response.json();}}).then(data=>{console.log(data);if(data.success){console.log('准备跳转到/home');this.$router.push('/home');}else{alert('登陆失败:'+data.message);}}).catch(error=>{console.error('登陆错误:',error);})},//输入时就验证,有错误信息就进行validata验证直到无错误为止validataOnInput(){if(this.error.username || this.error.pwd){this.validata();}},validata(){let isValid = true;if (this.username.length < 5) {this.error.username = '用户名不能小于5个字符';isValid = false;} else {this.error.username = '';}if (this.pwd.length < 5) {this.error.pwd = '密码不能小于5个字符';isValid = false;} else {this.error.pwd = '';}return isValid;}},};
</script>
<style module>
div.loginDiv
{margin: 0 auto;width: 500px;height: 300px;border: 1px solid black;border-radius: 15px;background-color:azure;opacity: 0.7;position: relative;top: 230px;z-index: 9;box-shadow: 0 10px 10px rgba(0,0,0,0.5);
}
div.loginDiv form,label,input
{margin:10px;position: relative;z-index: 99;opacity: 1;
}
div.listDiv
{width: 100%;height: 60px;vertical-align: middle;}
div.loginDiv label
{font-size: 16px;
}
div.loginDiv input
{width: 250px;height: 35px;font-size: 16px;text-indent:5px;
}
.btn
{width: 200px;height: 40px;background-color: rgb(20, 134, 20);color: white;font-size: 16px;border:none;border-radius: 5px;margin-left: 100px;margin-top: 20px;
}
.errorMessage
{display: block;heigth:20px;font-size: 12px;color:red;
}
</style>

下面是home.vue
这个就非常的简单

<template><div class="homeDiv"><h3>后台主页</h3><p>welcome to the home page</p></div>
</template>
<script>
export default{name:'Home'
}
</script>

然后将三个组件合并到路由中
login2.js

import Vue from 'vue';
import VueRouter from 'vue-router';
import App from './app.vue';
import Login from './components/Login.vue';
import Home from './components/Home.vue';Vue.use(VueRouter);//将组件写入路由中
const routes=[{path:'/',name:'Login',component:Login},{path:'/home',name:"Home",component:Home,	//这里的beforeEnter验证接口为api/check-auth,就是判断是否已经验证过的用户beforeEnter(to,from,next){fetch('http://localhost:3000/api/check-auth').then((response)=>{if(response.ok){return response.json();}}).then(data=>{console.log('验证结果:',data);if(data.isAthenticated){next();}else{next('/');}}).catch(error=>{console.log('验证错误:',error);next('/');});}}
];const router=new VueRouter({routes:routes
});
new Vue({el:"#app",router,render:h=>h(App)
})

//访问的主入口代码login2.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Vue Router example</title></head>
<body><div id="app"></div><script  type="module" src="./login2.js"></script>
</body>
</html>

现在你进入cmd并进入到你代码的根目录中用 node login.js 运行后端代码
然后开启第2个cmd并进入到代码根目录,使用vite进行热测试 运行 npm run dev 进行预览
你会看以一个登陆页面如:
在这里插入图片描述
上面的login.vue中是一个同步登陆过程
现在我们把改成异步登陆过程

<template><div :class="$style.loginDiv"><form @submit.prevent="handleSubmit" autocomplete="off"><div :class="$style.listDiv"><label for="username">&emsp;:</label><inputtype="text"id="username"v-model="username"required@blur="validateField('username')" //失焦就验证@input="debouncedValidate"		//防抖,输入验证autocomplete="false"><span v-show="errors.username" :class="$style.errorMessage">{{errors.username }}</span>    </div><div :class="$style.listDiv"><label for="pwd">&emsp;:</label><inputtype="password"id="pwd"v-model="pwd"required@blur="validateField('pwd')"@input="debouncedValidate"autocomplete="false"><span v-show="errors.pwd" :class="$style.errorMessage">{{errors.pwd}}</span>    </div><div :class="$style.listDiv"><button type="submit" :class="$style.btn" :disabled="isSubmitting">{{isSubmitting ? '验证中...' : '登陆'}}</button></div></form></div>
</template>
<script>
import {debounce} from 'lodash-es'; //防抖模块
export default{data(){return{username:'',pwd:'',isSubmitting:false,	//防重复提交errors:{username:'',pwd:''}}},//生命周期钩子函数,在实例创建后被调用,但dom还未生成时作用于数据上created(){//防抖函数,延迟(300ms)this.debouncedValidate=debounce(this.validateAll,300);//监听自定义验证事件this.$on('form-validate',this.validateAll);},beforeDestroy(){//移除事件监听this.$off('form-validate',this.validateAll);//取消防抖this.debouncedValidate.cancel();},methods:{async handleSubmit(){this.$emit('form-validate');    //触发全局验证const isValid=await this.validateAll();if(!isValid)return;this.isSubmitting=true;try{const response=await fetch('http://localhost:3000/api/login',{method:'post',headers:{'Content-type':"application/json"},body:JSON.stringify({username:this.username,pwd:this.pwd})});const data=await response.json();if(data.success){this.$router.push('/home');}else{alert(`${data.message}`);}}catch(error){console.error('登陆错误:',error);alert('网络请求失败,请重试');}finally{this.isSubmitting=false;}},async validateField(field){return new Promise((resolve,reject)=>{//$nextTick在 DOM 更新完成后执行某些操作,这里就是输入数据后进行验证,每输入1次验证一次this.$nextTick(()=>{let isValid=true;if(field==='username'){isValid=this.username.length>=5;this.errors.username=isValid ? "" : '用户名不能小于5个字符';}else if(field==='pwd'){isValid=this.pwd.length>=6;this.errors.pwd=isValid ? "" : '密码不能小于6个字符';}resolve(isValid);});});},async validateAll(){//合成期约,都是true时返回一个[true,true]的resolve数组,如果一个为false,则中断返回那个中断的错误信息const results=await Promise.all([this.validateField('username'),this.validateField('pwd')]);return results.every(result=>result);}},
}
</script><style module>
div.loginDiv
{margin: 0 auto;width: 500px;height: 300px;border: 1px solid black;border-radius: 15px;background-color:azure;opacity: 0.7;position: relative;top: 230px;z-index: 9;box-shadow: 0 10px 10px rgba(0,0,0,0.5);
}
div.loginDiv form,label,input
{margin:10px;margin-top:25px;position: relative;z-index: 99;opacity: 1;
}div.listDiv
{width: 100%;height: 60px;vertical-align: middle;margin-top: 15px;
}
div.listDiv span
{text-align: left;text-indent: 100px;width: 100%;
}
div.loginDiv label
{font-size: 16px;
}
div.loginDiv input
{width: 250px;height: 35px;font-size: 16px;text-indent:5px;
}
.btn
{width: 200px;height: 40px;background-color: rgb(20, 134, 20);color: white;font-size: 16px;border:none;border-radius: 5px;margin-left: 100px;margin-top: 20px;
}
.errorMessage
{display:block;font-size:12px;height:20px;    /*固定高度防止布局抖动*/color:red;
}
.btn:disabled{opacity:0.7;cursor:not-allowed;
}
</style>

现在把login2.js中的改成import Login from ‘./components/Login2.vue’;
就可以使用异步验证方法了

最后用vite来进行打包

http://www.lryc.cn/news/575387.html

相关文章:

  • 命名数据网络 | 数据包(Data Packet)
  • chili3d笔记23 正交投影3d重建笔记4 点到线2
  • 【NLP】使用 LangGraph 构建 RAG 的Research Multi-Agent
  • house of apple2
  • Linux系统(信号篇):信号的产生
  • 【Pandas】pandas DataFrame shift
  • Ubuntu下布署mediasoup-demo
  • 黑马JVM解析笔记(四):Javap图解指令流程,深入理解Java字节码执行机制
  • Redis 为什么选用跳跃表,而不是红黑树
  • 《聊一聊ZXDoc》之汽车标定、台架标定、三高标定
  • 【STM32】外部中断
  • 【C++11】右值引用和移动语义
  • gRPC 使用(python 版本)
  • 2025学年湖北省职业院校技能大赛 “信息安全管理与评估”赛项 样题卷(五)
  • Axure版TDesign 组件库-免费版
  • MQTT 和 HTTP 有什么本质区别?
  • 如何将 Memfault 固件 SDK 集成到使用 Nordic 的 nRF Connect SDK(NCS)的项目中
  • 数据结构进阶 - 第四,五章 串、数组和广义表
  • Docker 入门教程(一):从概念到第一个容器
  • 水质指数预测模型R²偏低的原因分析与优化策略
  • 2-深度学习挖短线股-1-股票范围选择
  • uniapp微信小程序:editor组件placeholder字体样式修改
  • vue3 + elementPlus 封装hook,检测form表单数据修改变更;示例用 script setup 语法使用
  • SpringBoot项目快速开发框架JeecgBoot——Web处理!
  • 一次开发,多端适配!全面掌握Dioxus跨平台开发框架!
  • 远程玩3A大作要多少帧?ToDesk、向日葵、UU远程性能对决
  • 面试破局:告别流水账,用“故事思维”重塑自我介绍
  • rocketmq中broker和namesrv的区别和联系?
  • 川翔云电脑全新上线:三维行业高效云端算力新选择
  • 智能化监管:微算法科技(NASDAQ:MLGO)比特币社区分类器助力加密货币市场规范发展