Skip to content

⏰ useCountDown - 时间倒计时管理神器

🌟 介绍

时间就是金钱!⏰ 在现代应用中,倒计时功能无处不在:秒杀活动、验证码倒计时、考试时间、活动截止时间... useCountDown 就是你的时间管理专家!

这个强大的 Hook 就像一个精准的瑞士手表,为你提供:

  • ⏱️ 精确计时 - 毫秒级精度,绝不误差
  • 🎮 完整控制 - 开始、暂停、重置,随心所欲
  • 📊 多维度显示 - 天、时、分、秒、毫秒,想要什么有什么
  • 🔔 智能回调 - 时间变化和结束事件,一个不漏

🎯 核心能力:

  • 倒计时管理 - 从设定时间开始,精确倒数到零
  • 🎮 状态控制 - 开始、暂停、重置,完全掌控时间流逝
  • 📱 实时更新 - 响应式数据,界面自动同步更新
  • 🚀 性能优化 - 智能渲染频率,节省系统资源

🚀 代码演示

⏰ 基本用法 - 经典倒计时

最常见的场景:活动倒计时显示

html
<template>
  <div class="countdown-demo">
    <div class="countdown-display">
      <!-- 🎨 精美的倒计时显示 -->
      <div class="time-block">
        <span class="time-value">{{ current.days }}</span>
        <span class="time-label">天</span>
      </div>
      <div class="time-separator">:</div>
      
      <div class="time-block">
        <span class="time-value">{{ current.hours }}</span>
        <span class="time-label">时</span>
      </div>
      <div class="time-separator">:</div>
      
      <div class="time-block">
        <span class="time-value">{{ current.minutes }}</span>
        <span class="time-label">分</span>
      </div>
      <div class="time-separator">:</div>
      
      <div class="time-block">
        <span class="time-value">{{ current.seconds }}</span>
        <span class="time-label">秒</span>
      </div>
    </div>
    
    <!-- 📊 详细信息显示 -->
    <div class="countdown-info">
      <p>⏱️ 总剩余时间:{{ formatTime(current.total) }}</p>
      <p>📅 剩余天数:{{ current.days }} 天</p>
      <p>🕐 剩余小时:{{ current.hours }} 小时</p>
      <p>⏰ 剩余分钟:{{ current.minutes }} 分钟</p>
      <p>⏱️ 剩余秒数:{{ current.seconds }} 秒</p>
    </div>
    
    <!-- 🎮 控制按钮 -->
    <div class="countdown-controls">
      <button @click="startCountdown" :disabled="isRunning">
        ▶️ 开始倒计时
      </button>
      <button @click="pauseCountdown" :disabled="!isRunning">
        ⏸️ 暂停倒计时
      </button>
      <button @click="resetCountdown">
        🔄 重置倒计时
      </button>
    </div>
  </div>
</template>
js
import { ref, computed } from 'vue';
import { useCountDown } from '@vant/use';

export default {
  setup() {
    const isRunning = ref(false);
    
    // ⏰ 创建倒计时实例 - 24小时倒计时
    const countDown = useCountDown({
      time: 24 * 60 * 60 * 1000, // 24小时 = 86,400,000 毫秒
      onChange: (current) => {
        // 🔔 每次时间变化时触发
        console.log('⏰ 时间更新:', {
          剩余天数: current.days,
          剩余小时: current.hours,
          剩余分钟: current.minutes,
          剩余秒数: current.seconds
        });
      },
      onFinish: () => {
        // 🎉 倒计时结束时触发
        isRunning.value = false;
        console.log('🎉 倒计时结束!时间到!');
        alert('⏰ 时间到!活动已结束!');
      }
    });
    
    // 🎮 控制方法
    const startCountdown = () => {
      countDown.start();
      isRunning.value = true;
      console.log('▶️ 倒计时开始!');
    };
    
    const pauseCountdown = () => {
      countDown.pause();
      isRunning.value = false;
      console.log('⏸️ 倒计时暂停!');
    };
    
    const resetCountdown = () => {
      countDown.reset();
      isRunning.value = false;
      console.log('🔄 倒计时重置!');
    };
    
    // 🎨 格式化时间显示
    const formatTime = (milliseconds) => {
      const seconds = Math.floor(milliseconds / 1000);
      const minutes = Math.floor(seconds / 60);
      const hours = Math.floor(minutes / 60);
      const days = Math.floor(hours / 24);
      
      if (days > 0) return `${days}天${hours % 24}小时`;
      if (hours > 0) return `${hours}小时${minutes % 60}分钟`;
      if (minutes > 0) return `${minutes}分钟${seconds % 60}秒`;
      return `${seconds}秒`;
    };

    return {
      current: countDown.current,
      isRunning,
      startCountdown,
      pauseCountdown,
      resetCountdown,
      formatTime
    };
  },
};

⚡ 毫秒级精度 - 高精度倒计时

当你需要极致精确的倒计时(比如体育比赛、游戏等场景):

html
<template>
  <div class="precision-countdown">
    <div class="precision-display">
      <!-- 🎯 高精度显示 -->
      <div class="main-time">
        {{ current.minutes.toString().padStart(2, '0') }}:{{ current.seconds.toString().padStart(2, '0') }}
      </div>
      <div class="milliseconds">
        .{{ Math.floor(current.milliseconds / 10).toString().padStart(2, '0') }}
      </div>
    </div>
    
    <div class="precision-info">
      <p>⚡ 毫秒级精度倒计时演示</p>
      <p>🎯 剩余毫秒:{{ current.milliseconds }}</p>
      <p>📊 总剩余:{{ current.total }}ms</p>
    </div>
    
    <div class="precision-controls">
      <button @click="startPrecision" :disabled="isPrecisionRunning">
        ⚡ 开始高精度倒计时
      </button>
      <button @click="pausePrecision" :disabled="!isPrecisionRunning">
        ⏸️ 暂停
      </button>
      <button @click="resetPrecision">
        🔄 重置
      </button>
    </div>
  </div>
</template>
js
import { ref } from 'vue';
import { useCountDown } from '@vant/use';

export default {
  setup() {
    const isPrecisionRunning = ref(false);
    
    // ⚡ 毫秒级精度倒计时 - 5分钟
    const precisionCountDown = useCountDown({
      time: 5 * 60 * 1000, // 5分钟
      millisecond: true, // 🔧 开启毫秒级渲染
      onChange: (current) => {
        // ⚡ 毫秒级更新回调
        if (current.seconds <= 10 && current.minutes === 0) {
          console.log(`⚠️ 最后 ${current.seconds}.${Math.floor(current.milliseconds / 100)} 秒!`);
        }
      },
      onFinish: () => {
        isPrecisionRunning.value = false;
        console.log('⚡ 高精度倒计时结束!');
        // 🎉 可以在这里添加特效或音效
      }
    });
    
    const startPrecision = () => {
      precisionCountDown.start();
      isPrecisionRunning.value = true;
      console.log('⚡ 高精度倒计时开始!');
    };
    
    const pausePrecision = () => {
      precisionCountDown.pause();
      isPrecisionRunning.value = false;
      console.log('⏸️ 高精度倒计时暂停!');
    };
    
    const resetPrecision = () => {
      precisionCountDown.reset();
      isPrecisionRunning.value = false;
      console.log('🔄 高精度倒计时重置!');
    };

    return {
      current: precisionCountDown.current,
      isPrecisionRunning,
      startPrecision,
      pausePrecision,
      resetPrecision
    };
  },
};

🛒 实战场景 - 秒杀倒计时

电商秒杀活动的经典应用:

html
<template>
  <div class="seckill-countdown">
    <div class="seckill-header">
      <h3>🔥 限时秒杀</h3>
      <div class="seckill-status" :class="{ active: isActive, ended: isEnded }">
        {{ statusText }}
      </div>
    </div>
    
    <div class="seckill-timer" v-if="!isEnded">
      <span class="timer-label">{{ timerLabel }}</span>
      <div class="timer-blocks">
        <div class="timer-block">
          <span class="timer-value">{{ current.hours.toString().padStart(2, '0') }}</span>
          <span class="timer-unit">时</span>
        </div>
        <div class="timer-separator">:</div>
        <div class="timer-block">
          <span class="timer-value">{{ current.minutes.toString().padStart(2, '0') }}</span>
          <span class="timer-unit">分</span>
        </div>
        <div class="timer-separator">:</div>
        <div class="timer-block">
          <span class="timer-value">{{ current.seconds.toString().padStart(2, '0') }}</span>
          <span class="timer-unit">秒</span>
        </div>
      </div>
    </div>
    
    <div class="seckill-product">
      <div class="product-info">
        <h4>🎮 游戏手柄 Pro</h4>
        <div class="price-info">
          <span class="original-price">¥299</span>
          <span class="seckill-price">¥99</span>
          <span class="discount">限时7折</span>
        </div>
      </div>
      
      <button 
        class="seckill-btn" 
        :class="{ 
          waiting: !isActive && !isEnded,
          active: isActive,
          ended: isEnded 
        }"
        :disabled="!isActive"
        @click="handleSeckill"
      >
        {{ buttonText }}
      </button>
    </div>
  </div>
</template>
js
import { ref, computed } from 'vue';
import { useCountDown } from '@vant/use';

export default {
  setup() {
    const isActive = ref(false);
    const isEnded = ref(false);
    
    // 🛒 秒杀倒计时 - 2小时
    const seckillCountDown = useCountDown({
      time: 2 * 60 * 60 * 1000, // 2小时
      onChange: (current) => {
        // 🔔 倒计时进行中
        if (current.total <= 60 * 1000 && !isActive.value) {
          // 最后1分钟,激活秒杀
          isActive.value = true;
          console.log('🔥 秒杀活动开始!最后1分钟!');
        }
      },
      onFinish: () => {
        // 🏁 秒杀结束
        isActive.value = false;
        isEnded.value = true;
        console.log('🏁 秒杀活动结束!');
      }
    });
    
    // 🎯 计算状态文本
    const statusText = computed(() => {
      if (isEnded.value) return '🏁 活动已结束';
      if (isActive.value) return '🔥 正在秒杀';
      return '⏰ 即将开始';
    });
    
    const timerLabel = computed(() => {
      if (isActive.value) return '🔥 秒杀结束倒计时';
      return '⏰ 秒杀开始倒计时';
    });
    
    const buttonText = computed(() => {
      if (isEnded.value) return '😢 活动已结束';
      if (isActive.value) return '🔥 立即秒杀';
      return '⏰ 等待开始';
    });
    
    // 🛒 处理秒杀点击
    const handleSeckill = () => {
      if (isActive.value) {
        console.log('🛒 用户点击秒杀!');
        alert('🎉 恭喜!秒杀成功!');
      }
    };
    
    // 🚀 自动开始倒计时
    seckillCountDown.start();

    return {
      current: seckillCountDown.current,
      isActive,
      isEnded,
      statusText,
      timerLabel,
      buttonText,
      handleSeckill
    };
  },
};

📱 验证码倒计时 - 实用工具

短信验证码发送的经典场景:

html
<template>
  <div class="verification-demo">
    <div class="phone-input">
      <label>📱 手机号码:</label>
      <input 
        v-model="phoneNumber" 
        type="tel" 
        placeholder="请输入手机号码"
        :disabled="isCodeSending"
      />
    </div>
    
    <div class="verification-section">
      <label>🔐 验证码:</label>
      <input 
        v-model="verificationCode" 
        type="text" 
        placeholder="请输入验证码"
        maxlength="6"
      />
      <button 
        class="send-code-btn"
        :class="{ disabled: !canSendCode }"
        :disabled="!canSendCode"
        @click="sendVerificationCode"
      >
        {{ codeButtonText }}
      </button>
    </div>
    
    <button class="verify-btn" @click="verifyCode">
      ✅ 验证
    </button>
  </div>
</template>
js
import { ref, computed } from 'vue';
import { useCountDown } from '@vant/use';

export default {
  setup() {
    const phoneNumber = ref('');
    const verificationCode = ref('');
    const isCodeSending = ref(false);
    const hasCodeSent = ref(false);
    
    // 📱 验证码倒计时 - 60秒
    const codeCountDown = useCountDown({
      time: 60 * 1000, // 60秒
      onChange: (current) => {
        console.log(`📱 验证码倒计时:${current.seconds}秒`);
      },
      onFinish: () => {
        hasCodeSent.value = false;
        console.log('📱 验证码倒计时结束,可以重新发送');
      }
    });
    
    // 🎯 计算是否可以发送验证码
    const canSendCode = computed(() => {
      return phoneNumber.value.length === 11 && !hasCodeSent.value && !isCodeSending.value;
    });
    
    // 🎨 按钮文本
    const codeButtonText = computed(() => {
      if (isCodeSending.value) return '📤 发送中...';
      if (hasCodeSent.value) return `⏰ ${codeCountDown.current.value.seconds}秒后重发`;
      return '📱 发送验证码';
    });
    
    // 📤 发送验证码
    const sendVerificationCode = async () => {
      if (!canSendCode.value) return;
      
      isCodeSending.value = true;
      console.log(`📱 向 ${phoneNumber.value} 发送验证码...`);
      
      try {
        // 🌐 模拟API调用
        await new Promise(resolve => setTimeout(resolve, 1000));
        
        // ✅ 发送成功
        hasCodeSent.value = true;
        codeCountDown.start(); // 开始倒计时
        console.log('✅ 验证码发送成功!');
        alert('📱 验证码已发送,请查收短信!');
        
      } catch (error) {
        console.error('❌ 验证码发送失败:', error);
        alert('❌ 验证码发送失败,请重试!');
      } finally {
        isCodeSending.value = false;
      }
    };
    
    // ✅ 验证验证码
    const verifyCode = () => {
      if (!verificationCode.value) {
        alert('🔐 请输入验证码!');
        return;
      }
      
      if (verificationCode.value.length !== 6) {
        alert('🔐 验证码应为6位数字!');
        return;
      }
      
      // 🎉 验证成功(这里应该调用实际的验证API)
      console.log('✅ 验证码验证成功!');
      alert('🎉 验证成功!');
      
      // 🔄 重置状态
      hasCodeSent.value = false;
      codeCountDown.reset();
      verificationCode.value = '';
    };

    return {
      phoneNumber,
      verificationCode,
      isCodeSending,
      canSendCode,
      codeButtonText,
      current: codeCountDown.current,
      sendVerificationCode,
      verifyCode
    };
  },
};

📚 API 参考

🔧 类型定义

ts
// ⏰ 当前时间信息
type CurrentTime = {
  days: number;         // 🗓️ 剩余天数
  hours: number;        // 🕐 剩余小时(0-23)
  total: number;        // ⏱️ 剩余总时间(毫秒)
  minutes: number;      // ⏰ 剩余分钟(0-59)
  seconds: number;      // ⏱️ 剩余秒数(0-59)
  milliseconds: number; // ⚡ 剩余毫秒(0-999)
};

// 🎮 倒计时控制器
type CountDown = {
  start: () => void;                    // ▶️ 开始倒计时
  pause: () => void;                    // ⏸️ 暂停倒计时
  reset: (totalTime?: number) => void;  // 🔄 重置倒计时
  current: ComputedRef<CurrentTime>;    // 📊 当前时间状态
};

// ⚙️ 配置选项
type UseCountDownOptions = {
  time: number;                              // ⏰ 倒计时时长(毫秒)
  millisecond?: boolean;                     // ⚡ 是否开启毫秒级渲染
  onChange?: (current: CurrentTime) => void; // 🔔 时间变化回调
  onFinish?: () => void;                     // 🏁 倒计时结束回调
};

function useCountDown(options: UseCountDownOptions): CountDown;

📋 参数说明

参数说明类型默认值
time⏰ 倒计时时长
💡 单位:毫秒(1秒 = 1000毫秒)
number-
millisecond⚡ 是否开启毫秒级渲染
💡 开启后更新频率更高,适合高精度场景
booleanfalse
onChange🔔 倒计时变化回调
💡 每次时间更新时触发
(current: CurrentTime) => void-
onFinish🏁 倒计时结束回调
💡 倒计时归零时触发
() => void-

🎮 返回值说明

参数说明类型
current📊 当前剩余时间
💡 响应式数据,自动更新
ComputedRef<CurrentTime>
start▶️ 开始倒计时
💡 从当前剩余时间开始计时
() => void
pause⏸️ 暂停倒计时
💡 保持当前时间,可恢复
() => void
reset🔄 重置倒计时
💡 可选择性传入新的时长
(time?: number): void

⏰ CurrentTime 详解

名称说明类型示例
total⏱️ 剩余总时间(毫秒)
💡 所有时间的毫秒总和
number86400000
days🗓️ 剩余天数
💡 完整的天数部分
number1
hours🕐 剩余小时
💡 当天剩余小时(0-23)
number12
minutes⏰ 剩余分钟
💡 当前小时剩余分钟(0-59)
number30
seconds⏱️ 剩余秒数
💡 当前分钟剩余秒数(0-59)
number45
milliseconds⚡ 剩余毫秒
💡 当前秒剩余毫秒(0-999)
number500

🎯 实际应用场景

🛒 电商场景

js
// 🔥 秒杀倒计时
const seckillTimer = useCountDown({
  time: getTimeUntilSeckill(), // 距离秒杀开始的时间
  onFinish: () => startSeckill() // 开始秒杀
});

// 🎁 限时优惠
const promotionTimer = useCountDown({
  time: promotionEndTime - Date.now(),
  onChange: (current) => {
    if (current.total < 60000) { // 最后1分钟
      showUrgentNotification();
    }
  }
});

📱 移动应用

js
// 📱 验证码倒计时
const smsTimer = useCountDown({
  time: 60 * 1000, // 60秒
  onFinish: () => enableResendButton()
});

// 🎮 游戏倒计时
const gameTimer = useCountDown({
  time: gameTimeLimit,
  millisecond: true, // 游戏需要高精度
  onFinish: () => endGame()
});

🏢 企业应用

js
// 📅 会议倒计时
const meetingTimer = useCountDown({
  time: timeUntilMeeting,
  onChange: (current) => {
    if (current.total <= 5 * 60 * 1000) { // 5分钟提醒
      sendMeetingReminder();
    }
  }
});

// ⏰ 工作时间倒计时
const workTimer = useCountDown({
  time: timeUntilWorkEnd,
  onFinish: () => showWorkEndNotification()
});

💡 最佳实践

✅ 推荐做法

  1. ⏰ 合理设置更新频率

    js
    // ✅ 一般场景使用秒级更新
    const normalTimer = useCountDown({
      time: targetTime,
      millisecond: false // 节省性能
    });
    
    // ✅ 高精度场景才使用毫秒级
    const precisionTimer = useCountDown({
      time: targetTime,
      millisecond: true // 仅在需要时开启
    });
  2. 🔔 善用回调函数

    js
    // ✅ 在关键时间点提醒用户
    const timer = useCountDown({
      time: targetTime,
      onChange: (current) => {
        if (current.total === 60000) { // 最后1分钟
          showUrgentAlert();
        }
        if (current.total === 10000) { // 最后10秒
          playCountdownSound();
        }
      },
      onFinish: () => {
        showCompletionMessage();
      }
    });
  3. 🎮 正确管理状态

    js
    // ✅ 配合组件状态使用
    const isActive = ref(false);
    const timer = useCountDown({
      time: targetTime,
      onFinish: () => {
        isActive.value = false; // 同步更新状态
      }
    });

❌ 避免的做法

  1. 🚫 不必要的毫秒级渲染

    js
    // ❌ 普通倒计时不需要毫秒级精度
    const timer = useCountDown({
      time: 24 * 60 * 60 * 1000,
      millisecond: true // 浪费性能
    });
  2. 🚫 忘记处理结束状态

    js
    // ❌ 没有处理倒计时结束
    const timer = useCountDown({
      time: targetTime
      // 缺少 onFinish 回调
    });
  3. 🚫 频繁重置倒计时

    js
    // ❌ 不要在每次渲染时重置
    // 应该在特定事件触发时重置

🛠️ 调试技巧

🔍 时间格式化工具

js
// 🎨 美化时间显示
const formatCountdown = (current) => {
  const { days, hours, minutes, seconds } = current;
  
  if (days > 0) {
    return `${days}天${hours}小时${minutes}分钟`;
  }
  if (hours > 0) {
    return `${hours}小时${minutes}分钟${seconds}秒`;
  }
  if (minutes > 0) {
    return `${minutes}分钟${seconds}秒`;
  }
  return `${seconds}秒`;
};

📊 倒计时监控

js
// 📈 监控倒计时状态
const monitorCountdown = (current) => {
  console.log('⏰ 倒计时状态:', {
    总剩余时间: `${current.total}ms`,
    格式化时间: formatCountdown(current),
    进度百分比: `${((initialTime - current.total) / initialTime * 100).toFixed(1)}%`
  });
};

📚 相关文档

⏰ 时间相关

🎮 状态管理

🛠️ 开发工具

💡 实战案例

基於Vant構建的企業級移動端解決方案