最直接可靠的方式是每100–200ms用setInterval调用new Date()更新DOM,避免1000ms间隔导致的时间跳变;关键在于高频刷新+只在秒变化时更新内容,并用padStart保证格式。

用 setInterval 配合 new Date() 实时更新 DOM
最直接可靠的方式是定时获取当前时间并写入元素,不依赖外部库,兼容所有现代浏览器。关键不是“怎么显示”,而是“怎么避免时间跳变或卡顿”。
常见错误是把 setInterval 设为 1000ms 却没处理毫秒进位——比如在 12:59:59.999 开始计时,下一秒可能延迟到 13:00:00.300 才触发,导致视觉上“卡半秒”。所以建议每 100–200ms 刷新一次,只更新秒及以后部分:
const clock = document.getElementById('clock');
function updateClock() {
const now = new Date();
const h = String(now.getHours()).padStart(2, '0');
const m = String(now.getMinutes()).padStart(2, '0');
const s = String(now.getSeconds()).padStart(2, '0');
const ms = String(Math.floor(now.getMilliseconds() / 10)).padStart(2, '0');
clock.textContent = `${h}:${m}:${s}.${ms}`;
}
updateClock(); // 立即执行一次,避免首屏空白
setInterval(updateClock, 100);- 用
padStart(2, '0')保证两位数格式,比now.getHours() + ''拼接更安全 - 毫秒只取前两位(百分之一秒),既够用又避免频繁重排
- 不要用
setTimeout递归代替setInterval,容易因 JS 主线程阻塞导致间隔漂移
用 requestAnimationFrame 更平滑但需手动对齐秒级变化
如果页面已有动画逻辑,或追求 60fps 一致性,requestAnimationFrame 比 setInterval 更省资源、更顺滑。但它不保证每秒触发 60 次,也不和系统时钟对齐,所以必须自己检测“是否到了新一秒”。
典型陷阱是直接在 rAF 回调里无条件调用 new Date(),结果每帧都生成新对象,GC 压力大;或者漏掉“整秒切换”的判断,导致秒数滞后。
立即学习“前端免费学习笔记(深入)”;
let lastSec = -1;
function animateClock() {
const now = new Date();
const currentSec = now.getSeconds();
if (currentSec !== lastSec) {
lastSec = currentSec;
const h = String(now.getHours()).padStart(2, '0');
const m = String(now.getMinutes()).padStart(2, '0');
clock.textContent = `${h}:${m}:${String(currentSec).padStart(2, '0')}`;
}
requestAnimationFrame(animateClock);
}
animateClock();- 只在秒数真正变化时更新 DOM,减少重绘
- 用
lastSec缓存上一次秒值,而不是靠间隔猜时间 - 不适合需要显示毫秒的场景——rAF 触发频率不稳定,无法精确到 10ms 级别
服务端时间同步:避免用户本地时钟不准导致显示错误
纯前端方案依赖用户设备系统时间,一旦用户手动改过时间(比如跨时区飞行后没同步),new Date() 就不可信。金融、打卡、倒计时类应用必须和服务端时间对齐。
做法不是“每秒请求一次服务器”,而是首次加载时用 AJAX 获取服务端时间戳,计算客户端与服务端的偏移量,后续用本地 Date 加偏移校准:
let timeOffset = 0;
fetch('/api/time')
.then(r => r.json())
.then(data => {
const serverTime = new Date(data.timestamp); // 假设接口返回 ISO 字符串或毫秒数
const clientTime = new Date();
timeOffset = serverTime.getTime() - clientTime.getTime();
});
<p>function getServerTime() {
return new Date(Date.now() + timeOffset);
}- 偏移量只需算一次,后续所有时间都基于它校准
- 接口响应头里的
Date字段也可读取,但需注意时区解析歧义 - 长期运行的页面要考虑服务端时间漂移(比如 NTP 同步),可每 5–10 分钟重新校准一次
CSS 动画不能替代 JS 更新时间,但能辅助视觉反馈
有人试图用 @keyframes 和 animation-delay 模拟秒针走动,这行不通——CSS 动画无法读取实时时间,也不能响应秒数变化。但可以用它做微交互增强,比如让数字翻转时带淡入或缩放效果。
关键限制是:CSS 无法绑定动态内容,所有样式变化必须由 JS 控制 class 或内联 style 触发。一个实用组合是 JS 更新文本后,加个临时 class 触发 CSS 过渡:
.clock-updating {
animation: pulse 0.3s ease-out;
}
@keyframes pulse {
from { opacity: 0.7; transform: scale(0.98); }
to { opacity: 1; transform: scale(1); }
}然后在 JS 中:
clock.classList.remove('clock-updating');
void clock.offsetWidth; // 强制重排,确保 class 移除生效
clock.textContent = '14:22:05';
clock.classList.add('clock-updating');- 不用监听 DOM 变化,主动控制 class 更可控
- 过渡时间控制在 200–400ms,太长会显得拖沓,太短看不出效果
- 避免对整个
<div id="clock">做 transform,否则可能触发全层重绘
真实项目里,时间显示从来不是“写完就跑”,而是得想清楚:要不要保精度、要不要抗篡改、要不要低功耗、要不要适配 SSR。最简单的 setInterval 模板够用,但一旦用户说“时间总比手机慢 20 秒”,就得回头补服务端校准逻辑了。



















