⏰ 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 | ⚡ 是否开启毫秒级渲染 💡 开启后更新频率更高,适合高精度场景 | boolean | false |
| onChange | 🔔 倒计时变化回调 💡 每次时间更新时触发 | (current: CurrentTime) => void | - |
| onFinish | 🏁 倒计时结束回调 💡 倒计时归零时触发 | () => void | - |
🎮 返回值说明
| 参数 | 说明 | 类型 |
|---|---|---|
| current | 📊 当前剩余时间 💡 响应式数据,自动更新 | ComputedRef<CurrentTime> |
| start | ▶️ 开始倒计时 💡 从当前剩余时间开始计时 | () => void |
| pause | ⏸️ 暂停倒计时 💡 保持当前时间,可恢复 | () => void |
| reset | 🔄 重置倒计时 💡 可选择性传入新的时长 | (time?: number): void |
⏰ CurrentTime 详解
| 名称 | 说明 | 类型 | 示例 |
|---|---|---|---|
| total | ⏱️ 剩余总时间(毫秒) 💡 所有时间的毫秒总和 | number | 86400000 |
| days | 🗓️ 剩余天数 💡 完整的天数部分 | number | 1 |
| hours | 🕐 剩余小时 💡 当天剩余小时(0-23) | number | 12 |
| minutes | ⏰ 剩余分钟 💡 当前小时剩余分钟(0-59) | number | 30 |
| seconds | ⏱️ 剩余秒数 💡 当前分钟剩余秒数(0-59) | number | 45 |
| milliseconds | ⚡ 剩余毫秒 💡 当前秒剩余毫秒(0-999) | number | 500 |
🎯 实际应用场景
🛒 电商场景
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()
});💡 最佳实践
✅ 推荐做法
⏰ 合理设置更新频率
js// ✅ 一般场景使用秒级更新 const normalTimer = useCountDown({ time: targetTime, millisecond: false // 节省性能 }); // ✅ 高精度场景才使用毫秒级 const precisionTimer = useCountDown({ time: targetTime, millisecond: true // 仅在需要时开启 });🔔 善用回调函数
js// ✅ 在关键时间点提醒用户 const timer = useCountDown({ time: targetTime, onChange: (current) => { if (current.total === 60000) { // 最后1分钟 showUrgentAlert(); } if (current.total === 10000) { // 最后10秒 playCountdownSound(); } }, onFinish: () => { showCompletionMessage(); } });🎮 正确管理状态
js// ✅ 配合组件状态使用 const isActive = ref(false); const timer = useCountDown({ time: targetTime, onFinish: () => { isActive.value = false; // 同步更新状态 } });
❌ 避免的做法
🚫 不必要的毫秒级渲染
js// ❌ 普通倒计时不需要毫秒级精度 const timer = useCountDown({ time: 24 * 60 * 60 * 1000, millisecond: true // 浪费性能 });🚫 忘记处理结束状态
js// ❌ 没有处理倒计时结束 const timer = useCountDown({ time: targetTime // 缺少 onFinish 回调 });🚫 频繁重置倒计时
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)}%`
});
};📚 相关文档
⏰ 时间相关
- useEventListener - 事件监听管理
- usePageVisibility - 页面可见性检测
- useRaf - 动画帧管理
🎮 状态管理
- useToggle - 布尔值状态切换
- useClickAway - 点击外部监听
- useWindowSize - 窗口尺寸监听
🛠️ 开发工具
- 组合式 API 介绍 - 了解更多实用 Hook
- 事件处理最佳实践 - 事件处理技巧
- 性能优化指南 - 性能优化方法