在 Vue3 项目开发中,验证码倒计时、按钮防重复提交等场景非常常见。如果直接把倒计时逻辑写在组件里,会导致多个页面重复维护一套定时器代码,既不优雅也容易出错。利用 Vue3 Composition API 提供的 Hooks 机制,可以把倒计时逻辑抽离成独立函数,配合 ElementPlus 按钮组件即可快速复用。
下面以 ElementPlus 的 el-button 为例,演示如何封装一个通用的 useCountDown Hooks,并实现点击按钮显示倒计时、倒计时结束自动恢复初始状态的效果。
一、编写 useCountDown Hooks
在项目 src/hooks 目录下新建 useCountDown.js。该 Hooks 的核心职责是管理倒计时秒数、倒计时状态和定时器生命周期。
- // hooks/useCountDown.js
- import { ref, watch } from "vue";
- // 默认倒计时时长 60 秒,也可按需传入
- export function useCountDown(num = 60) {
- // 倒计时剩余秒数
- const count = ref(num);
- // 是否处于倒计时中
- const isDown = ref(false);
- // 定时器 id
- let timerId = null;
- // 开始倒计时
- const start = () => {
- // 防止重复点击导致多个定时器叠加
- if (isDown.value) return;
- isDown.value = true;
- timerId = setInterval(() => {
- count.value--;
- }, 1000);
- };
- // 监听 count,归零时清除定时器并重置状态
- watch(count, (newCount) => {
- if (newCount <= 0) {
- clearInterval(timerId);
- count.value = num; // 重置为初始秒数
- isDown.value = false; // 标记倒计时结束
- }
- });
- return {
- count,
- isDown,
- start
- };
- }
复制代码
这里有一个细节值得注意:start 函数内部首先判断 isDown.value 是否为 true,如果当前已经在倒计时中,则直接返回。这样能有效防止用户连点时创建多个 setInterval,避免倒计时速度异常。
watch 监听 count 的作用是自动完成结束后的清理与状态复位。当 count 减到 0 时,清除定时器、把 count 重置为最初传入的 num,并把 isDown 置回 false。这样组件无需额外关心重置逻辑,下一次点击 start 又能从完整时长开始倒计时。
二、在 ElementPlus 页面中使用
在业务组件的模板中,使用 el-button 绑定点击事件,并利用 isDown 控制按钮显示文本。倒计时中显示“正在倒计时(剩余秒数)”,否则显示“开始倒计时”。
- <!-- xxx/index.vue -->
- <template>
- <el-button type="primary" @click="countDownFn">
- <span v-if="isDown">正在倒计时({{ count }})</span>
- <span v-else>开始倒计时</span>
- </el-button>
- </template>
- <script setup>
- // 导入 Hooks 函数
- import { useCountDown } from "@/hooks/useCountDown";
- // 传入 10 秒,也可以改为 60 秒满足验证码场景
- const { count, isDown, start } = useCountDown(10);
- // 点击按钮时触发 start
- const countDownFn = () => {
- start();
- };
- </script>
- <style lang="scss" scoped>
- /* 按需编写样式 */
- </style>
复制代码
如果实际场景是“发送验证码”按钮,只需要把 useCountDown(10) 改为 useCountDown(60),并把 el-button 的 :disabled 属性绑定为 isDown,即可同时获得 60 秒倒计时与禁用状态:
- <el-button type="primary" :disabled="isDown" @click="countDownFn">
- {{ isDown ? `重新发送(${count}s)` : "获取验证码" }}
- </el-button>
复制代码
三、扩展思路
由于 useCountDown 返回的是响应式对象,其他组件也可以直接调用。若要实现“发送成功后才开始倒计时”,可以在发送接口成功回调里调用 start(),而不是绑定在按钮点击时立刻启动。这样能避免用户输错号码、接口报错后仍盲目进入倒计时状态。
另外,若要在倒计时结束后执行额外操作(如跳转页面),可以在 watch 回调中判断 newCount <= 0 后,再通过回调参数或自定义事件通知组件。不过示例代码没有包含这部分逻辑,实际开发时可以根据项目需求灵活扩展。
这套 Hooks + ElementPlus 的组合方案,代码量少、逻辑清晰,适合用于验证码倒计时、营销活动限时按钮、短信重发等常见交互场景。 |