vue3发送验证码倒计时功能的实现(防止连点、封装复用)
奥特曼 人气:0一、实现思路
二、实现一个简单的验证码倒计时
//倒计时初始变量 const codeNum = ref(60); // 定时器id let clearId: number; // 发送验证码 const sendCode = async () => { // 防止下次点击 如果倒计时的时间不是60 就不执行下面逻辑 if (codeNum.value != 60) return; // 掉接口 const res = await getCode(mobile.value, "login"); // 把定时器赋值给 变量clearId 目的:清除定时器 clearId= setInterval(() => { // 每次 时间1s -1 codeNum.value--; // 时间=0时 清除定时器 if (codeNum.value == 0) { clearInterval(clearId); // 还原 倒计时60s codeNum.value = 60; } }, 1000); };
当然 这只是没有做过优化的一个发送验证码,如果要考虑点击连续点击或者离开页面时销毁定时器 还要加一些功能
三、优化
(1)第一种方案,定义一个变量来控制 如果之前没有点击 再次点击不再执行
<script lang="ts" setup> // 接口 import { getCode } from "@/api/login"; // 定时器id let clearId:number; // 倒计时时间 const codeNum = ref(60); // 手机号 const mobile = ref("13230000001"); // 是否发送了验证码 防止连点 + const isClickSend = ref(false); // 发送验证码 const sendCode = async () => { + if (isClickSend.value || codeNum.value != 60) return; isClickSend.value = true; const res = await getCode(mobile.value, "login"); clearId.value = setInterval(() => { codeNum.value--; if (codeNum.value == 0) { clearInterval(clearId.value); codeNum.value = 60; + isClickSend.value = false; } }, 1000); console.log("sendCode", res); }; </script> <template> <a href="javascript:;" rel="external nofollow" rel="external nofollow" @click="sendCode" >{{ codeNum == 60 ? "发送验证码" : `(${codeNum})发送验证码` }}</a> </template>
(2)第二种方案. 让倒计时初始值为0 调用函数时在赋值为60 下次值大于0时同样不再执行,实现思路和第一种相似
const codeNum = ref(0); const sendCode = async () => { if (codeNum.value > 0) return; isClickSend.value = true; const res = await getCode(mobile.value, "login"); codeNum.value = 60 if(clearId) clearInterval(clearId) clearId = setInterval(() => { codeNum.value--; if (codeNum.value == 0) { clearInterval(clearId); } }, 1000); };
其中没有对手机号进行校验 若需要则自己可以写校验规则,也可以参考当前使用的其他组件库使用
离开页面销毁定时器
onMounted(() => { clearInterval(clearId) })
四、逻辑封装
为什么要封装 验证码倒计时功能?
1. 为了下次再次使用时 直接copy代码达到复用
2. 在日常开发中可能 有很多场景都需要发送验证码 只是 接口一样 只是参数的type值不一样 例如 登录需要传login 注册需要传register 到时候只需要调用更换参数即可
新建composable/index.ts 准备放公共方法
// 引用 发送的验证码类型 import type { CodeType } from '@/type/user' // 引入接口 import { getCode } from "@/api/login"; import type { Ref } from 'vue' // 引入vant form类型 用来初始化form类型 可参考vant 若没有使用 则删除 import type { FormProps, FormInstance } from 'vant'; // 封装方法 只需要传入手机号、 type类型 export const useSendCode = (mobile: Ref<string>, type: CodeType) => { // 定义定时器初始值为0 const timer = ref(0) // 定义form变量 如果用了vant 记得要给vanForm 绑定ref const form = ref<FormInstance | null>() ; // 定义定时器id 为了清除定时器 let timerId: number // 之后页面调用send方法来使用 const send = async () => { // 第二次点击 大于0时 直接 return if (timer.value > 0) return // 校验 mobile字段 要和 van-field 中的name保持一直 否则校验失败 如果校验失败则不走下面代码 注意await await form.value?.validate('mobile') // 校验通过调用接口 await getCode(mobile.value, type) // 赋值倒计时 可修改成自己需要的时间 timer.value = 10 // 如果之前id存在可清除 if (timerId) clearInterval(timerId) // 赋值定时器id timerId = setInterval(() => { // 时间-1 timer.value-- // 倒计时结束 清除定时器 if (timer.value == 0) clearInterval(timerId) }, 1000) } // onMounted(() => { clearInterval(timerId) }) return { timer, send, form } }
由于代码中使用了插件 没有引入ref onMounted 需要可自行引入
页面中使用
<script lang="ts" setup> import { mobileRule } from "@/utils/rule"; import { useSendCode } from "@/composable"; const { send, timer, form } = useSendCode(mobile, "login"); </script> <template> <van-form ref="form" @submit="pwdLogin"> <van-field v-model="mobile" name="mobile" maxlength="11" placeholder="请输入手机号" :rules="mobileRule" /> </van-form> ... <a href="javascript:;" rel="external nofollow" rel="external nofollow" @click="sendCode" > {{ timer == 0 ? "发送验证码" : `(${timer})后发送验证码` }} </a> </template>
补充 mobileRule
import type { FieldRule } from 'vant' const mobileRules: FieldRule[] = [ { required: true, message: '请输入手机号' }, { pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确' } ] const passwordRules: FieldRule[] = [ { required: true, message: '请输入密码' }, { pattern: /^\w{8,24}$/, message: '密码需8-24个字符' } ] const codeRules: FieldRule[] = [ { required: true, message: '请输入验证码' }, { pattern: /^\d{6}$/, message: '验证码为6位数字' } ] export { mobileRules, passwordRules, codeRules }
加载全部内容