应通过一次请求获取服务器时间戳并计算时差补偿,而非高频轮询;用 date.now() + serveroffset 动态计算服务器时间,结合秒数变更检测更新dom,避免跳秒或粘滞,并按服务器时区格式化显示。

客户端时间永远不等于服务器时间,直接用 new Date() 显示的只是浏览器本地时间。要动态显示服务器端时间,核心是「校准」:先获取一次服务器当前时间戳,再在客户端持续补偿时差并递增,而不是反复请求接口。
为什么不能每秒发 AJAX 请求服务器时间
高频轮询会显著增加服务器压力,尤其在并发用户多时;HTTP 请求本身有网络延迟和响应抖动,拿到的时间戳可能已偏差 100–500ms;多数 HTTP 服务端未针对单点时间查询做性能优化,容易成为瓶颈。
- 每次请求都携带完整 HTTP 头、Cookie,开销远大于一个毫秒级时间值
- 若后端返回的是字符串(如
"2026-08-26T12:01:05+08:00"),客户端还需额外解析,进一步引入误差 - 移动端弱网环境下,请求失败或超时会导致时钟卡顿甚至跳变
推荐做法:首次请求时间戳 + 客户端补偿
页面加载时,通过一次轻量请求(如 GET /api/time)获取服务器当前 Unix 时间戳(单位:毫秒),同时记录客户端发起请求的 Date.now() 值。两者相减得出初始时差 serverOffset。
- 后端应直接返回数字型时间戳,例如:
1756209665123,避免字符串解析 - 客户端保存
serverOffset = serverTimestamp - clientRequestTime - 后续每秒执行:
const serverNow = Date.now() + serverOffset,再格式化显示 - 可选:每隔 5–10 分钟重新校准一次,抵消客户端时钟漂移
setInterval 更新 vs requestAnimationFrame
用 setInterval(update, 1000) 看似合理,但实际执行间隔受 JS 主线程阻塞影响,可能延迟几十毫秒,导致秒针“粘滞”。requestAnimationFrame 虽更顺滑,但它按帧率触发(通常 60fps),无法保证每秒只更新一次——你看到的可能是连续两帧都显示同一秒,下一帧突然跳两秒。
- 真正稳定的做法是:在定时回调中检查
Math.floor((Date.now() + offset) / 1000)是否变化,仅当秒数变更时才重绘 DOM - 这样既避免高频无意义更新,又防止因执行延迟导致的跳秒
- 务必在页面隐藏(
document.hidden === true)时暂停定时器,否则后台标签页仍会消耗 CPU
容易被忽略的关键细节
服务器时间通常为 UTC 或固定时区(如 Asia/Shanghai),而 toLocaleTimeString() 默认使用客户端时区。若后端返回的是 UTC 时间戳,却用 new Date(serverNow).toLocaleTimeString() 直接渲染,结果会错乱——比如服务器是 UTC+8 的 12:00,客户端在纽约就会显示成 00:00。
- 统一方案:后端明确告知时区(如通过 HTTP Header
X-Server-Timezone: Asia/Shanghai),前端用Intl.DateTimeFormat指定时区格式化 - 更简单做法:后端直接返回带时区的 ISO 字符串(如
"2026-08-26T12:01:05+08:00"),客户端用new Date(isoString)解析,它能自动处理偏移 - 不要依赖
Date.prototype.getTimezoneOffset()手动加减,它不可靠且易出错
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











