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">用 户:</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">密 码:</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">登 陆</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">用 户:</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">密 码:</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来进行打包