正确做法是用 ref 存储目标时间戳并实时计算剩余毫秒数;暂停/恢复需控制定时器生命周期并修正时间差;更新应基于系统时间而非累减,且组件卸载时须清理定时器。
倒计时的秒数递减不能靠“每秒减 1”这种粗略累减,否则会因定时器延迟、JS 主线程阻塞或页面失焦导致跳秒甚至错乱。正确做法是:用 ref 存储目标时间戳(如 targetTime = Date.now() + 60000),再通过实时计算 targetTime - Date.now() 得到剩余毫秒数 remainingMs。这个 remainingMs 必须是 ref,才能触发响应式更新。
暂停不是“把数字停住”,而是停止定时器;恢复是重新启动并同步修正时间差。关键点:
isRunning = ref(false) 标记当前是否活跃clearInterval(timerId.value),并保存最后一次更新的时间戳(lastUpdate = Date.now())remainingMs 中扣除,再启动新定时器setInterval(..., 1000),建议用 100ms 或结合 requestAnimationFrame 提升感知精度单纯每 100ms 执行 remainingMs.value -= 100 会因执行延迟产生漂移。应每次更新都重新读取系统时间:
now = Date.now()
delta = now - lastTimestamp.value
remainingMs.value = Math.max(0, remainingMs.value - delta)
lastTimestamp.value = now
Vue 3 中使用 onBeforeUnmount 清除定时器,防止内存泄漏和后台继续执行:
timerId = ref(null) 存在 setup 内部onBeforeUnmount(() => { clearInterval(timerId.value); }) 中统一清理document.addEventListener('visibilitychange', ...) 中联动暂停