倒计时通用组件
组件功能说明:
- 接收
endTime属性: 组件接收一个endTime属性,用于设置倒计时的到期时间。endTime可以是Date对象、时间戳或格式化的时间字符串。 - 支持插槽自定义显示内容:
- 默认插槽:用于显示倒计时,可以访问
days、hours、minutes、seconds数据。 ended插槽:用于在倒计时结束后显示自定义内容。
- 默认插槽:用于显示倒计时,可以访问
- 倒计时结束时通知父组件: 当倒计时结束时,组件会触发
countdown-ended事件,父组件可以监听该事件并调用getNewEndTime方法获取最新的到期时间。 - 重新开始倒计时: 父组件获取到最新的到期时间后,更新
endTime属性,组件会自动重新开始倒计时。 - 清除定时器: 组件在销毁前会清除定时器,避免对页面产生负担。
代码:
<template>
<div v-if="timeRemaining > 0">
<slot :days="days" :hours="hours" :minutes="minutes" :seconds="seconds">
{{ days }} 天 {{ hours }} 小时 {{ minutes }} 分钟 {{ seconds }} 秒
</slot>
</div>
<div v-else>
<slot name="ended"> 倒计时结束 </slot>
</div>
</template>
<script>
export default {
name: 'Countdown',
props: {
endTime: {
type: [Date, String, Number],
required: true,
},
},
data() {
return {
timeRemaining: 0,
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
timer: null,
};
},
mounted() {
this.startCountdown();
},
beforeDestroy() {
this.clearTimer();
},
methods: {
startCountdown() {
this.updateCountdown();
this.timer = setInterval(this.updateCountdown, 1000);
},
clearTimer() {
clearInterval(this.timer);
this.timer = null;
},
updateCountdown() {
const endTime = new Date(this.endTime).getTime();
const now = Date.now();
this.timeRemaining = endTime - now;
if (this.timeRemaining <= 0) {
this.clearTimer();
this.$emit('countdown-ended');
return;
}
this.days = Math.floor(this.timeRemaining / (1000 * 60 * 60 * 24));
this.hours = Math.floor((this.timeRemaining % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
this.minutes = Math.floor((this.timeRemaining % (1000 * 60 * 60)) / (1000 * 60));
this.seconds = Math.floor((this.timeRemaining % (1000 * 60)) / 1000);
},
},
};
</script>
注意:
- 在
getNewEndTime方法中,需要自行实现获取最新到期时间的逻辑,例如调用后端接口。 - 组件使用了
setInterval函数,每秒更新一次倒计时。 - 组件在
beforeDestroy生命周期钩子函数中清除了定时器,确保组件销毁时不会继续占用资源。
用setTimeout 函数替代 setInterval 来实现倒计时功能。
思路:
- 在
updateCountdown函数结束时,判断timeRemaining是否大于 0。 - 如果大于 0,则使用
setTimeout递归调用updateCountdown函数自身,延迟时间设置为 1000 毫秒(1 秒)。 - 如果小于等于 0,说明倒计时结束,清除定时器,并触发
countdown-ended事件。
代码:
<template>
<div v-if="timeRemaining > 0">
<slot :days="days" :hours="hours" :minutes="minutes" :seconds="seconds">
{{ days }} 天 {{ hours }} 小时 {{ minutes }} 分钟 {{ seconds }} 秒
</slot>
</div>
<div v-else>
<slot name="ended"> 倒计时结束 </slot>
</div>
</template>
<script>
export default {
name: 'Countdown',
props: {
endTime: {
type: [Date, String, Number],
required: true,
},
},
data() {
return {
timeRemaining: 0,
days: 0,
hours: 0,
minutes: 0,
seconds: 0,
timer: null,
};
},
mounted() {
this.startCountdown();
},
beforeDestroy() {
this.clearTimer();
},
methods: {
startCountdown() {
this.updateCountdown();
},
clearTimer() {
clearTimeout(this.timer);
this.timer = null;
},
updateCountdown() {
const endTime = new Date(this.endTime).getTime();
const now = Date.now();
this.timeRemaining = endTime - now;
if (this.timeRemaining <= 0) {
this.clearTimer();
this.$emit('countdown-ended');
return;
}
this.days = Math.floor(this.timeRemaining / (1000 * 60 * 60 * 24));
this.hours = Math.floor((this.timeRemaining % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
this.minutes = Math.floor((this.timeRemaining % (1000 * 60 * 60)) / (1000 * 60));
this.seconds = Math.floor((this.timeRemaining % (1000 * 60)) / 1000);
// 使用 setTimeout 递归调用 updateCountdown
this.timer = setTimeout(this.updateCountdown, 1000);
},
},
};
</script>
变化:
- 使用
setTimeout替换了setInterval。 - 在
updateCountdown函数内部,使用setTimeout递归调用自身,实现每秒更新倒计时。 - 在
clearTimer函数中,使用clearTimeout清除setTimeout设置的定时器。
使用 setTimeout 的方式可以避免一些 setInterval 潜在的问题,例如:
setInterval不会检查代码执行是否完成,如果代码执行时间超过了设置的间隔时间,可能会导致定时器堆积,影响性能。setTimeout则会在每次代码执行完成后才设置下一次定时,更加安全可靠。
组件使用方法:
<template>
<div>
<Countdown :endTime="endTime" @countdown-ended="getNewEndTime">
<template #default="{ days, hours, minutes, seconds }">
距离活动结束还有:{{ days }}天{{ hours }}小时{{ minutes }}分钟{{ seconds }}秒
</template>
<template #ended>
活动已结束!
</template>
</Countdown>
</div>
</template>
<script>
export default {
data() {
return {
endTime: '2024-01-01', // 设置初始到期时间
};
},
methods: {
getNewEndTime() {
// 此处调用接口获取最新的到期时间
// 并更新 this.endTime
// ...
},
},
};
</script>