以实务来说,总是会有一些情况导致使用者没办法正常收到认证码,所以系统必须具备 retry on failure 的功能,让使用者可以发送重新发送认证码的需求。但使用者频繁要求重新发送时又会造成服务器的负担,所以我们要让在两次发送期间要加入等待一段时间後才能再发送的限制。
在先前的范例中,使用者如果连点按钮就会造成 Request 重复送出,为了防止这种情况,我们可以采用以下几种做法:
修改 BindMailForm.vue 如下:
<script setup>
import {onMounted, ref, defineEmits} from 'vue'
import {sendValidationCodePost} from '/src/service/api'
import * as yup from 'yup';
const userName = ref("");
const userToken = ref("");
const userEmail = ref("");
const inputEmail = ref("");
// 增加一个状态变数判断表单是否正在送出
const onSubmit = ref(false);
const emit = defineEmits(['nextStep'])
const mailSchema = yup.string().email().required();
const submit = async (mail) => {
// 表单请求送出时
onSubmit.value = true;
const isMailValid = await mailSchema.isValid(mail);
if (isMailValid) {
sendValidationCodePost(userName.value, mail, userToken.value)
.then((res) => {
console.log("res: ", res);
const result = res.data.message;
emit('nextStep', result);
})
.catch((err) => {
console.log("err: ", err);
})
.finally(() => {onSubmit.value = false}); // 表单请求处理结束
} else {
onSubmit.value = false; // 表单请求处理结束
alert('请输入有效的信箱地址');
}
}
onMounted(() => {
liff.ready.then(() => {
const user = liff.getDecodedIDToken();
userName.value = user && user.name;
userToken.value = user && user.sub;
userEmail.value = user && user.email;
})
})
</script>
<template>
<template v-if="userEmail">
<p>将发送身份认证码到 {{ userEmail }}</p>
// 当 onSubmit 为 true 时帮按钮加上 disabled
<button type="button" class="btn" @click="submit(userEmail)" :disabled="onSubmit">确定</button>
</template>
<template v-else>
<p>发送身份认证码到 <input type="email" v-model="inputEmail" placeholder="请输入 Email"></p>
// 当 onSubmit 为 true 时帮按钮加上 disabled
<button type="button" class="btn" @click="submit(inputEmail)" :disabled="onSubmit">确定</button>
</template>
</template>
使用一个 z-index: 9999
的 loading mask 覆盖全页面,可让使用者知道正在读取中,也防止使用者点击页面其他部分造成流程异常。
Debounce 又称去抖动,将连续触发合并成一个事件,可延迟事件执行,在连续触发时只执行一次,改善效能。
Throttle 又称函式节流,顾名思义就是会限制函式的呼叫频率,减少过快的呼叫达到节流,避免过度消耗资源。
可用 Lodash 实现这两种功能。
每次发送 Request 前先判断是否还有 pending 中的同类请求,若存在则不发送 Request,或者取消先前 pending 中的 Request。
axios 中的 cancel token 就是不错的实作方式
实作方式很简单,添加一个计时器,在 API 回应我们成功送出验证信时 disabled 按钮,并开始倒数一分钟,倒数完毕才解除 disabled 状态。
一般 JavaScript 要计时都会用 setTimeout / setInterval,但其实这两者的计时并不精准,详情可参考以下两位大大的文章:
所以这次就使用 requestAnimationFrame 来实作倒数计时功能吧~
修改 BindMail.vue
<script setup>
import {onMounted, ref, provide} from 'vue'
import BindMailForm from "./BindMailForm.vue";
import BindMailResult from "./BindMailResult.vue";
const errorMsg = ref("");
const bindStep = ref('form');
const resMessage = ref('');
provide('resMessage', resMessage);
const next = (event) => {
resMessage.value = event
bindStep.value = 'result'
}
// 加入 back step
const back = () => {
resMessage.value = ""
bindStep.value = 'form'
}
const initializeApp = () => {
if (!liff.isLoggedIn() || !liff.isInClient()) {
errorMsg.value = "please use line liff open";
}
};
onMounted(() => {
liff.init({
liffId: 'YOUR_LIFF_ID'
}).then(() => {
initializeApp();
}).catch((err) => {
errorMsg.value = "initialize LIFF fail";
});
});
</script>
<template>
<h1>验证码小帮手 - 身份认证</h1>
// 绑定 backStep emit event
<component v-if="!errorMsg" :is="(bindStep === 'form') ? BindMailForm : BindMailResult" @backStep="back" @nextStep="next"></component>
<p v-else class="error">{{ errorMsg }}</p>
</template>
修改 BindMailResult.vue
<template>
<p>{{(result === 'success') ? '已将验证码发送至信箱' : '发送失败,请稍後再试'}}</p>
<div class="inline-btns">
// 增加再次发送的按钮
<button type="button" class="btn" @click="emit('backStep')" :disabled="backDisabled">
再次发送
<span v-if="interval < 60000">({{60 - Math.floor(interval/1000)}})</span>
</button>
<button type="button" class="btn" @click="closeLiff">关闭</button>
</div>
</template>
<script setup>
import {ref, inject, onMounted} from "vue";
const start = ref(null);
const interval = ref(0);
const backDisabled = ref(true);
const emit = defineEmits(['backStep', 'nextStep']);
const result = inject('resMessage');
const closeLiff = () => {
liff.closeWindow();
}
// 使用 requestAnimationFrame 实作倒数计时的功能
const countDown = (timestamp) => {
if (!start.value) start.value = timestamp;
interval.value = timestamp - start.value;
if (interval.value < 60000) {
window.requestAnimationFrame(countDown);
} else {
backDisabled.value = false;
}
}
onMounted(() => {
start.value = null;
window.requestAnimationFrame(countDown);
})
</script>
有成功禁用按钮 & 倒数计时~
铁人赛也即将迈入尾声了,这礼拜忙到爆,文章都是慢慢补完 Orz
希望接下来两天可以正常发文~
<<: Day13 Function Component的生命周期 - UseEffect
>>: Day28. Blue Prism最安全的管家 -BP自动登入Gmail
前面讲了一对现在怎又多了CLI? CLI有事要干嘛的? 在之前我们写Vue时都是用vue.js和&l...
威尔猪刚开始学设计时,也曾被老师说你的阴影不行,多观察生活周遭。What...? 当时真的不懂,只...
那我们要开始着手处理我们的资料集了,今天会先做资料前处理的部分,其实不管是机器学习或是深度学习,只要...
今天是连假第一天,返乡的看官们都平安吧!前几天苹果发表了 New iPhone,售价降低了,加上今...
此时新版本为 1.14.5, 所以在2021/11/9时 有更新文章部分内容。 为什麽要架设狗狗币全...