设计思路一:
-
使用 Vue Router 的组件钩子:
- Vue Router 提供了在路由切换时触发的钩子,如
beforeRouteLeave
和beforeRouteEnter
,这可以帮助我们在路由切换时控制倒计时的暂停和恢复。 - 通过在
beforeRouteLeave
钩子中暂停倒计时,在beforeRouteEnter
或mounted
钩子中恢复倒计时。
- Vue Router 提供了在路由切换时触发的钩子,如
-
路由切换时的暂停与恢复:
- 暂停倒计时 :在离开当前路由时,通过
beforeRouteLeave
钩子暂停倒计时。 - 恢复倒计时 :在进入当前路由时,通过
beforeRouteEnter
或mounted
钩子恢复倒计时。
- 暂停倒计时 :在离开当前路由时,通过
-
保持倒计时状态:
- 利用
localStorage
或sessionStorage
,也可以在页面切换时保存倒计时状态(例如剩余时间),然后在切回该页面时恢复。
- 利用
示例
js
<template>
<div>
<p>倒计时:{{ formattedTime }}</p>
</div>
</template>
<script>
import { ref, computed, onMounted, onBeforeRouteLeave, onBeforeRouteEnter } from 'vue';
export default {
props: {
duration: {
type: Number,
required: true, // 总时长(毫秒)
},
},
setup(props, { emit }) {
const remainingTime = ref(props.duration); // 剩余时间
const timerId = ref(null); // 定时器 ID
const lastTimestamp = ref(null); // 上次更新时间戳
// 格式化倒计时为 HH:mm:ss
const formattedTime = computed(() => {
const seconds = Math.floor((remainingTime.value / 1000) % 60);
const minutes = Math.floor((remainingTime.value / 1000 / 60) % 60);
const hours = Math.floor(remainingTime.value / 1000 / 60 / 60);
return `${hours.toString().padStart(2, '0')}:${minutes
.toString()
.padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
});
// 更新倒计时
const updateCountdown = () => {
if (lastTimestamp.value === null) {
lastTimestamp.value = Date.now();
}
const now = Date.now();
const elapsedTime = now - lastTimestamp.value;
lastTimestamp.value = now;
remainingTime.value -= elapsedTime;
if (remainingTime.value <= 0) {
remainingTime.value = 0;
clearInterval(timerId.value);
timerId.value = null;
emit('finished'); // 倒计时完成事件
}
};
// 启动倒计时
const startCountdown = () => {
if (!timerId.value) {
lastTimestamp.value = Date.now();
timerId.value = setInterval(updateCountdown, 100);
}
};
// 暂停倒计时
const pauseCountdown = () => {
if (timerId.value) {
clearInterval(timerId.value);
timerId.value = null;
}
};
// 路由离开时暂停倒计时
onBeforeRouteLeave(() => {
pauseCountdown(); // 暂停倒计时
});
// 路由进入时恢复倒计时
onBeforeRouteEnter((to, from, next) => {
next((vm) => {
startCountdown(); // 恢复倒计时
});
});
// 生命周期钩子
onMounted(() => {
startCountdown(); // 组件挂载时启动倒计时
});
return {
formattedTime,
};
},
};
</script>
示例解析
-
倒计时更新逻辑:
remainingTime
:存储剩余时间。formattedTime
:用来格式化显示剩余时间,按HH:mm:ss
格式显示。updateCountdown
:每 100 毫秒更新一次倒计时,并计算时间差。
-
Vue Router 钩子:
onBeforeRouteLeave
:当路由切换离开当前页面时,暂停倒计时。onBeforeRouteEnter
:当进入当前页面时,恢复倒计时。由于onBeforeRouteEnter
钩子在组件挂载前执行,因此需要通过next
回调来启动倒计时。
-
生命周期钩子:
onMounted
:在组件挂载时启动倒计时。
设计思路总结
-
Vue Router 钩子:
- 使用
onBeforeRouteLeave
在路由切换时暂停倒计时。 - 使用
onBeforeRouteEnter
在路由切换后恢复倒计时。
- 使用
-
倒计时状态管理:
- 使用
setInterval
进行倒计时更新,确保在组件的生命周期内倒计时是实时更新的。 - 在组件卸载时通过清除定时器来避免内存泄漏。
- 使用
-
页面恢复时的倒计时同步:
- 在路由进入时恢复倒计时,确保不会丢失剩余时间。
-
内存管理:
- 每次路由切换时暂停和恢复倒计时,确保资源不会浪费。
设计思路二:
-
倒计时核心逻辑:
- 使用
setInterval
或requestAnimationFrame
来实时更新倒计时。 - 利用
Date.now()
获取当前时间,确保倒计时在暂停后能正确恢复。 - 页面切换时通过
visibilitychange
事件暂停倒计时,切回时恢复。
- 使用
-
页面切换处理:
- 使用
document.hidden
和visibilitychange
事件监听页面是否可见。 - 如果页面隐藏,则暂停倒计时。
- 如果页面恢复,则恢复倒计时,并计算时间差。
- 使用
-
倒计时结束处理:
- 当倒计时结束时触发事件,通知父组件。
示例
js
<template>
<div>
<p>倒计时:{{ formattedTime }}</p>
</div>
</template>
<script>
import { ref, computed, onMounted, onUnmounted } from 'vue';
export default {
props: {
duration: {
type: Number,
required: true, // 倒计时总时长(毫秒)
},
},
setup(props, { emit }) {
const remainingTime = ref(props.duration); // 剩余时间
const timerId = ref(null); // 定时器 ID
const lastTimestamp = ref(null); // 上次更新时间戳
// 格式化显示倒计时
const formattedTime = computed(() => {
const seconds = Math.floor((remainingTime.value / 1000) % 60);
const minutes = Math.floor((remainingTime.value / 1000 / 60) % 60);
const hours = Math.floor(remainingTime.value / 1000 / 60 / 60);
return `${hours.toString().padStart(2, '0')}:${minutes
.toString()
.padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
});
// 更新倒计时
const updateCountdown = () => {
if (lastTimestamp.value === null) {
lastTimestamp.value = Date.now();
}
const now = Date.now();
const elapsedTime = now - lastTimestamp.value;
lastTimestamp.value = now;
remainingTime.value -= elapsedTime;
if (remainingTime.value <= 0) {
remainingTime.value = 0;
clearInterval(timerId.value);
timerId.value = null;
emit('finished'); // 倒计时完成事件
}
};
// 启动倒计时
const startCountdown = () => {
if (!timerId.value) {
lastTimestamp.value = Date.now();
timerId.value = setInterval(updateCountdown, 100);
}
};
// 暂停倒计时
const pauseCountdown = () => {
if (timerId.value) {
clearInterval(timerId.value);
timerId.value = null;
}
};
// 页面切换时暂停或恢复倒计时
const handleVisibilityChange = () => {
if (document.hidden) {
pauseCountdown(); // 页面隐藏时暂停
} else {
startCountdown(); // 页面恢复时继续
}
};
onMounted(() => {
startCountdown(); // 初始化倒计时
document.addEventListener('visibilitychange', handleVisibilityChange); // 监听页面切换
});
onUnmounted(() => {
clearInterval(timerId.value); // 清理定时器
document.removeEventListener('visibilitychange', handleVisibilityChange); // 移除事件监听
});
return {
formattedTime,
};
},
};
</script>
示例解析
-
倒计时核心逻辑:
remainingTime
:保存当前的剩余时间。timerId
:用于保存setInterval
的定时器 ID,以便暂停时可以清除。lastTimestamp
:保存上次更新时间的时间戳,用于计算时间差。formattedTime
:计算剩余时间并格式化为HH:mm:ss
格式。
-
倒计时更新:
updateCountdown
:每 100 毫秒更新一次倒计时,计算时间差并更新剩余时间。- 使用
Date.now()
获取当前时间,通过时间差计算减少的时间量。
-
页面切换处理:
- 使用
visibilitychange
事件检测页面是否可见。当页面不可见时,调用pauseCountdown()
暂停倒计时;当页面恢复时,调用startCountdown()
恢复倒计时。
- 使用
-
生命周期管理:
- 在
onMounted
中启动倒计时并绑定事件监听。 - 在
onUnmounted
中清除定时器并移除事件监听,防止内存泄漏。
- 在
设计思路总结
-
倒计时精度:
- 使用
Date.now()
来计算时间差,确保即使倒计时被暂停或恢复,剩余时间计算也不会出现误差。
- 使用
-
页面切换处理:
- 通过
visibilitychange
事件来处理页面可见性状态,在页面切换时暂停或恢复倒计时。 - 使用
document.hidden
判断页面是否隐藏。
- 通过
-
内存管理:
- 在组件卸载时清除定时器和事件监听器,避免内存泄漏。