Vue.js组件实现多种场景倒计时功能
在前端应用开发中,倒计时功能是一种非常常见的需求,例如短信验证码计时、活动截止计时或任务限定时间提醒等。Vue.js 提供了强大的响应式数据绑定和组件生命周期管理能力,使得实现各种倒计时逻辑变得直观且高效。
短信验证码60秒倒计时
短信验证码场景下的倒计时通常是点击按钮后开始60秒计时,期间按钮变为不可用状态并显示剩余时间,计时结束后按钮恢复可点击状态。
<template>
<div>
<button v-if="!isSmsTimerActive" @click="startSmsCountdown">获取验证码</button>
<button v-else disabled>{{ remainingSmsSeconds }}s 后重试</button>
</div>
</template>
<script>
export default {
data() {
return {
remainingSmsSeconds: 60, // 初始倒计时秒数
isSmsTimerActive: false, // 标识倒计时是否进行中
smsIntervalId: null // 定时器ID
};
},
methods: {
startSmsCountdown() {
// 在实际应用中,这里会先发送验证码请求
// 只有请求成功后才启动倒计时
this.isSmsTimerActive = true;
this.remainingSmsSeconds = 60; // 每次启动重置为60秒
this.smsIntervalId = setInterval(() => {
if (this.remainingSmsSeconds > 0) {
this.remainingSmsSeconds--;
} else {
// 倒计时结束
clearInterval(this.smsIntervalId);
this.isSmsTimerActive = false;
this.smsIntervalId = null;
// 可在此处添加倒计时结束后的其他逻辑,如提示用户重发
}
}, 1000);
},
// 停止倒计时的方法,确保在组件销毁时清除定时器
stopSmsCountdown() {
if (this.smsIntervalId) {
clearInterval(this.smsIntervalId);
this.smsIntervalId = null;
}
this.isSmsTimerActive = false;
this.remainingSmsSeconds = 60;
}
},
// 在组件销毁前清除定时器,避免内存泄漏
beforeUnmount() { // Vue 3 uses beforeUnmount, Vue 2 uses beforeDestroy
this.stopSmsCountdown();
}
};
</script>
固定时长倒计时(例如15分钟)
这种倒计时通常用于显示一个固定时间段的剩余时间,例如活动结束、订单支付时限等。通过计算总秒数并利用计算属性格式化显示。
<template>
<div>
<p>距离结束还剩:<span>{{ formattedMinutes }}:{{ formattedSeconds }}</span></p>
</div>
</template>
<script>
export default {
data() {
return {
totalTimeInSeconds: 15 * 60, // 初始总时长,例如15分钟
fixedDurationIntervalId: null
};
},
computed: {
// 格式化分钟数,确保两位显示
formattedMinutes() {
const minutes = Math.floor(this.totalTimeInSeconds / 60);
return minutes < 10 ? '0' + minutes : String(minutes);
},
// 格式化秒数,确保两位显示
formattedSeconds() {
const seconds = this.totalTimeInSeconds % 60;
return seconds < 10 ? '0' + seconds : String(seconds);
}
},
mounted() {
this.startFixedDurationCountdown();
},
methods: {
startFixedDurationCountdown() {
// 避免重复创建定时器
if (this.fixedDurationIntervalId) return;
this.fixedDurationIntervalId = setInterval(() => {
if (this.totalTimeInSeconds > 0) {
this.totalTimeInSeconds--;
} else {
// 倒计时结束
clearInterval(this.fixedDurationIntervalId);
this.fixedDurationIntervalId = null;
console.log('固定时长倒计时已结束!');
// 可在此处触发结束事件或执行其他操作
}
}, 1000);
},
// 停止固定时长倒计时
stopFixedDurationCountdown() {
if (this.fixedDurationIntervalId) {
clearInterval(this.fixedDurationIntervalId);
this.fixedDurationIntervalId = null;
}
}
},
beforeUnmount() { // Vue 3 uses beforeUnmount, Vue 2 uses beforeDestroy
this.stopFixedDurationCountdown();
}
};
</script>
支持页面跳转不重置、结束自动续期的倒计时(例如5分钟)
对于需要在页面跳转或刷新后保持倒计时状态,并且在倒计时结束后自动续期(例如,每次倒计时结束或用户首次进入页面都从5分钟开始)的场景,我们可以结合 localStorage 来实现。
<template>
<div>
<p>当前会话剩余:<span>{{ persistentFormattedMinutes }}:{{ persistentFormattedSeconds }}</span></p>
<button @click="handlePageNavigation">前往其他页面</button>
</div>
</template>
<script>
// 定义LocalStorage键名和倒计时的默认时长
const SESSION_END_TIMESTAMP_KEY = 'session_countdown_end_time';
const SESSION_DURATION_SECONDS = 5 * 60; // 5分钟
export default {
data() {
return {
currentRemainingSessionSeconds: 0, // 当前剩余秒数
sessionIntervalId: null, // 定时器ID
};
},
computed: {
// 格式化分钟数
persistentFormattedMinutes() {
const minutes = Math.floor(this.currentRemainingSessionSeconds / 60);
return minutes < 10 ? '0' + minutes : String(minutes);
},
// 格式化秒数
persistentFormattedSeconds() {
const seconds = this.currentRemainingSessionSeconds % 60;
return seconds < 10 ? '0' + seconds : String(seconds);
}
},
created() {
this.initSessionCountdown();
},
methods: {
initSessionCountdown() {
let storedEndTime = localStorage.getItem(SESSION_END_TIMESTAMP_KEY);
let currentTime = Date.now(); // 当前时间戳(毫秒)
let calculatedEndTime;
if (storedEndTime) {
calculatedEndTime = parseInt(storedEndTime, 10);
if (calculatedEndTime > currentTime) {
// 如果存储的结束时间在未来,则继续倒计时
this.currentRemainingSessionSeconds = Math.floor((calculatedEndTime - currentTime) / 1000);
} else {
// 如果存储的结束时间已过期,则重新开始并续期
calculatedEndTime = currentTime + SESSION_DURATION_SECONDS * 1000;
localStorage.setItem(SESSION_END_TIMESTAMP_KEY, calculatedEndTime);
this.currentRemainingSessionSeconds = SESSION_DURATION_SECONDS;
console.log('会话倒计时已过期,自动续期5分钟。');
}
} else {
// 首次进入页面,没有存储的结束时间,则开始新的倒计时
calculatedEndTime = currentTime + SESSION_DURATION_SECONDS * 1000;
localStorage.setItem(SESSION_END_TIMESTAMP_KEY, calculatedEndTime);
this.currentRemainingSessionSeconds = SESSION_DURATION_SECONDS;
console.log('首次启动会话倒计时,设置为5分钟。');
}
this.startSessionTimerLoop();
},
startSessionTimerLoop() {
if (this.sessionIntervalId) return; // 避免重复创建定时器
this.sessionIntervalId = setInterval(() => {
if (this.currentRemainingSessionSeconds > 0) {
this.currentRemainingSessionSeconds--;
} else {
// 倒计时归零,自动续期
let newEndTime = Date.now() + SESSION_DURATION_SECONDS * 1000;
localStorage.setItem(SESSION_END_TIMESTAMP_KEY, newEndTime);
this.currentRemainingSessionSeconds = SESSION_DURATION_SECONDS;
console.log('会话倒计时再次续期5分钟!');
}
}, 1000);
},
// 停止会话倒计时
stopSessionTimer() {
if (this.sessionIntervalId) {
clearInterval(this.sessionIntervalId);
this.sessionIntervalId = null;
}
},
// 模拟页面导航,确保倒计时状态被保留
handlePageNavigation() {
if (this.$router) {
this.$router.push({ name: 'AnotherRoute' }); // 替换为你的路由名称
} else {
console.warn('Vue Router 未初始化。无法执行页面跳转。');
}
}
},
beforeUnmount() { // Vue 3 uses beforeUnmount, Vue 2 uses beforeDestroy
this.stopSessionTimer();
},
};
</script>