先写一个用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}
//连接后端API
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(); //返回json数据
}
})
.then(data=>{
console.log(data);
if(data.success)
{
console.log('准备跳转到/home');
//路由跳转到home
this.$router.push('/home');
}else
{
alert('登陆失败:'+data.message);
}
})
.catch(error=>{
console.error('登陆错误:',error);
})
}
}
};
//组件home
const 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>
<input
type="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>
<input
type="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来进行打包