活动页做倒计时,用户刷新一下就回到 10 分钟,切个标签页回来数字又对不上。写的时候是每秒减一,页面一进后台就飘了。

根源是 setInterval 不保证准时。标签页进入后台,浏览器会把定时器压到一秒甚至一分钟一次,减出来的剩余时间和真实时间越差越远。

正确做法是存截止时间

页面加载时算出一个固定的结束时间戳,之后每次渲染都拿它减当前时间。这样刷新、切后台、锁屏都不影响结果。

var end = new Date('2026-10-26T20:00:00').getTime(); function tick() { var left = Math.max(0, end - Date.now()); render(left); }

渲染用 requestAnimationFrame 或一秒一次的 setInterval 都行,反正每次重新算,误差不累积。差值除以 1000 取整得到剩余秒数,再拆成天时分秒。

日期字符串的坑

new Date 解析带横杠的日期字符串时,老版本 Safari 会返回 Invalid Date。换成斜杠更稳,或者干脆直接传时间戳。服务器和客户端时区不一致时,用带时区的格式,别写成本地时间字符串。

实际会遇到的情况

  • 用户改了系统时间,倒计时会跟着跳。要求严格的场景,剩余时间以服务端返回为准。
  • 手机锁屏后页面被冻结,回到前台要立刻重算一次,别等下一个 tick。
  • 剩余归零后清掉定时器,否则定时器一直空转。

倒计时看着简单,关键就一句:存截止时间,别存剩余秒数。