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}
                //连接后端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">&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>
                <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">&emsp;:</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来进行打包

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值