前端技巧分享:Vue 3 项目中封装高效倒计时 Hook 及其应用实践

引言

现代 Web 开发 中,倒计时功能已成为提升用户体验的关键组件之一。无论是在电商平台的限时抢购、社交媒体的实时互动,还是在线服务的验证码获取,倒计时功能都在其中扮演着至关重要的角色。它们不仅能够提醒用户注意时间限制,还能增加紧迫感,促使用户采取行动。然而,传统的倒计时实现方式可能会遇到性能瓶颈、难以维护和复用等问题。

针对这些挑战,本文将深入探讨如何在 Vue 3 项目中封装一个高效且易于维护的倒计时 Hook ------ useCountdown,以解决传统方法的局限。

实践体验

useCountdown Hook 实现

ts 复制代码
import { ref, unref, onUnmounted } from "vue";

export function useCountdown(duration: number, onCountdownEnd?: () => void) {
    const time = ref(duration);
    const isTiming = ref(false);
    let timer: ReturnType<typeof setInterval> | null;

    const clear = () => {
        if (timer) {
            clearInterval(timer);
            timer = null;
        }
    }

    const stop = () => {
        clear();
        isTiming.value = false;
    }

    const reset = () => {
        stop();
        time.value = duration;
    }

    const start = () => {
        if (unref(isTiming) || !!timer) {
            return;
        }
        isTiming.value = true;
        timer = setInterval(() => {
            if (unref(time) <= 0) {
                reset();
                onCountdownEnd && onCountdownEnd()
            } else {
                time.value--;
            }
        }, 1000)
    }

    onUnmounted(() => {
        reset()
    })

    return {time, isTiming, start, stop, reset} as const;
}

useCountdown Hook 思路

倒计时 Hook 需满足以下核心功能需求,以实现灵活且可定制的倒计时行为:

  1. 总时长设定(duration
    • 用户需提供倒计时的总时长(duration),以秒为单位。
  2. 倒计时结束回调(onCountdownEnd
    • 当倒计时结束时,触发用户定义的回调函数(onCountdownEnd)。
  3. 返回值
    • Hook需返回以下两个值:
      • time:剩余的倒计时时长。
      • isTiming:表示倒计时是否正在进行中。
  4. 控制方法
    • 提供以下方法以控制倒计时行为:
      • start:开始倒计时。
      • stop:停止倒计时。
      • reset:重置倒计时至初始状态。

通过这些方法,用户可以精确控制倒计时的启动、停止和重置,使其更好地适应不同的业务场景和需求.

useCountdown Hook 应用实践

获取验证码按钮

  • 获取验证码按钮需要满足以下需求
    • 自定义倒计时时长 :通过duration属性,您可以设定倒计时的持续时间。
    • 自动禁用状态disabled属性结合倒计时状态isTiming自动处理按钮的禁用状态,防止用户在倒计时期间重复点击。
    • 异步校验方法validateBeforeSendingCode属性允许您定义一个异步函数,用于在倒计时开始前进行用户自定义的前置校验和发送验证码的API调用。
    • 自定义展示内容 :支持slot分发,允许您自定义按钮显示内容,同时提供timeisTiming属性供您使用。
ts 复制代码
<script setup lang="ts">
  import { useCountdown } from "../hooks/useCountdown";
  // https://code.juejin.cn/api/raw/7445202794878861348?id=7445202794878877732
  import { computed, PropType } from "vue@3.5.13";

  const props = defineProps({
    duration: {
      type: Number,
      default: 60
    },
    disabled: {
      type: Boolean,
      default: false
    },
    validateBeforeSendingCode: {
      type: Function as PropType<()=>Promise<boolean>>,
      default: ()=>Promise.resolve(true)
    }
  })
  const { time, isTiming, start } = useCountdown(props.duration);
  const isDisabled = computed(()=>{
    return props.disabled || isTiming.value;
  })
  const handleClick = async () => {
    const res = await props.validateBeforeSendingCode();
    res && start();
  }
</script>

<template>
  <button :disabled="isDisabled" @click="handleClick">
    <slot :time="time" :isTiming="isTiming"></slot>
    <template v-if="!$slots.default">
      {{isTiming ? `${time}s 后重试` : "获取验证码"}}
    </template>
  </button>
</template>

<style scoped>
  button {
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    background-color: #409eff;
    color: #ffffff;
    font-size: 14px;
    cursor: pointer;
    outline: none;
  }

  button+button {
    margin-left: 10px;
  }

  button:hover {
    background-color: #66b1ff;
  }

  button:active {
    background-color: #288ae2;
  }

  button[disabled] {
    cursor: not-allowed;
    color: #fff;
    background-color: #a0cfff;
    border-color: #a0cfff;
  }
</style>

倒计时自动登录

倒计时结束,触发用户自定义回调来实现

ts 复制代码
<script setup lang="ts">
import { useCountdown } from "../hooks/useCountdown"
// https://code.juejin.cn/api/raw/7445202794878861348?id=7445202794878877732
const handleCountdownEnd = () => {
  reset(); // 重置倒计时
  // do something
  // 跳转逻辑
}
const { time, reset, start } = useCountdown(3, handleCountdownEnd);
start();
</script>

<template>
  <button @click="handleCountdownEnd">{{time}}s 自动跳转登录</button>
</template>

<style scoped>

</style>

感谢阅读,敬请斧正!

相关推荐
GIS好难学6 分钟前
《Vue进阶教程》第六课:computed()函数详解(上)
前端·javascript·vue.js
nyf_unknown9 分钟前
(css)element中el-select下拉框整体样式修改
前端·css
m0_5485147719 分钟前
前端打印功能(vue +springboot)
前端·vue.js·spring boot
执键行天涯25 分钟前
element-plus中的resetFields()方法
前端·javascript·vue.js
一个努力学习的小男孩27 分钟前
【自学】Vues基础
vue.js
Days205031 分钟前
uniapp小程序增加加载功能
开发语言·前端·javascript
喵喵酱仔__32 分钟前
vue 给div增加title属性
前端·javascript·vue.js
dazhong201241 分钟前
HTML前端开发-- Iconfont 矢量图库使用简介
前端·html·svg·矢量图·iconfont
m0_748248771 小时前
前端vue使用onlyoffice控件实现word在线编辑、预览(仅列出前端部分需要做的工作,不包含后端部分)
前端·vue.js·word
莫惊春1 小时前
HTML5 第五章
前端·html·html5