正确做法是每次渲染前重新计算剩余毫秒数再格式化显示,使用 setinterval 每500ms更新一次,并在 diff === 0 时清除定时器防止内存泄漏。

用 setInterval 实现基础倒计时,但别直接写死毫秒数
倒计时本质是「当前时间」和「目标时间」的差值持续更新。直接用 setTimeout 递归或写死 1000ms 轮询都不可靠——系统卡顿、页面切后台、JS 执行延迟都会导致跳秒或漏秒。
正确做法是每次渲染前重新计算剩余毫秒数,再格式化显示:
const targetTime = new Date('2025-12-31T23:59:59').getTime();
function updateCountdown() {
const now = Date.now();
const diff = Math.max(0, targetTime - now);
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((diff % (1000 * 60)) / 1000);
document.getElementById('countdown').textContent =
`${days}d ${hours}h ${minutes}m ${seconds}s`;
}
setInterval(updateCountdown, 500); // 每500ms算一次,视觉更稳
注意:用 500ms 而非 1000ms,能减少因 JS 执行延迟导致的“卡半秒”感;Math.max(0, ...) 防止倒计时结束后出现负数。
倒计时结束时必须手动清除 setInterval,否则内存泄漏
很多人只顾启动倒计时,忘了它终会归零。一旦 diff === 0,不清理定时器,updateCountdown 仍会每 500ms 执行,占用 CPU 且可能触发后续逻辑错误。
- 在
updateCountdown函数内加判断:if (diff -
timerId必须是全局或闭包可访问的变量,不能声明在函数内部 - 如果倒计时需支持多次重启,每次调用前先
clearInterval原 ID,再赋新值
页面被切换到后台时,setInterval 会严重降频,要用 visibilitychange 补偿
Chrome/Firefox 在标签页不可见时会把 setInterval 限制为最低 1s 甚至更慢,导致倒计时“突然跳好几秒”。这不是 bug,是浏览器节电策略。
- 监听
document.addEventListener('visibilitychange', ...) - 当
document.hidden === true时,记录当前时间戳;恢复可见时,立即重算差值并更新 UI - 不要依赖后台期间的定时器回调,它不可信
服务端时间不同步会导致倒计时偏差,前端无法自愈
用户本地时间可能快 5 分钟或慢 3 分钟,而你用 new Date() 全部基于客户端时钟。电商抢购、活动开售这类场景,必须和服务端对齐。
- 首次加载时用 AJAX 请求服务端返回标准时间戳(如
/api/time返回{ timestamp: 1735689240123 }) - 之后所有倒计时计算都以该时间戳为基准,加上客户端与服务端的偏移量(
serverTime - Date.now()) - 别试图用
Date.prototype.setTimezoneOffset()修正——它只改显示,不改getTime()结果
本地时间不准是常态,不是边缘情况。只要倒计时精度要求高于 ±10 秒,就必须走服务端授时。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











