应使用 requestanimationframe 驱动时钟更新,每帧读取 date.now() 计算毫秒(now % 1000),仅在秒变化时重格式化时间字符串,并用 textcontent 更新以确保性能与安全。

用 setInterval 每 10 毫秒更新一次,但别真这么干
直接设 setInterval(update, 10) 看似能刷毫秒,实际会严重拖慢页面:浏览器重绘频率通常只有 60fps(约 16.7ms 一帧),频繁 DOM 写入反而导致卡顿、时间跳变,且用户根本感知不到 10ms 级别变化。
真正可行的做法是「视觉上显示毫秒,逻辑上只在秒级变化时重算」:
- 用
requestAnimationFrame驱动循环,每帧读取Date.now() - 只在当前秒数(
getSeconds())变化时,才重新格式化整个时间字符串 - 毫秒部分用
Date.now() % 1000实时计算,不依赖getMilliseconds()(它可能滞后) - 避免对每个数字都调用
padStart(3, '0'),毫秒固定三位,直接String(ms).padStart(3, '0')
textContent 比 innerHTML 更快更安全
毫秒级高频更新下,innerHTML 会触发 HTML 解析和子树重建,开销远高于纯文本写入。尤其当容器里只有时间字符串时,textContent 是唯一合理选择。
常见错误是混用:document.getElementById('clock').innerHTML = timeStr —— 这不仅慢,还可能因字符串含 或 <code>" 引发意外解析或 XSS(哪怕当前没风险,也埋下隐患)。
- 始终用
textContent更新纯时间文本 - 如果后续要加图标或样式标签(如
<span class="ms">123</span>),就改用innerHTML,但必须严格转义或白名单控制内容 - 测试时可对比
performance.now(),你会发现textContent更新耗时稳定在 0.02ms 以内,innerHTML波动常超 0.1ms
格式化函数要避开 getMilliseconds() 的坑
new Date().getMilliseconds() 返回的是「当前 Date 对象创建时刻的毫秒值」,不是「此刻的毫秒值」。当你在 setInterval 中每 100ms 调用一次,拿到的其实是上一次构造 Date 时的毫秒,误差可能达 ±100ms。
正确做法是统一用 Date.now() 计算:
- 先调
const now = Date.now() - 毫秒部分 =
now % 1000 - 秒部分 =
Math.floor(now / 1000) % 60 - 分、时、日等再用
new Date(now)提取,确保所有字段来自同一时间戳 - 别用
toLocaleTimeString()带毫秒 —— 它不支持毫秒输出,且格式不可控
示例关键片段:
function updateClock() {
const now = Date.now();
const ms = now % 1000;
const date = new Date(now);
const h = String(date.getHours()).padStart(2, '0');
const m = String(date.getMinutes()).padStart(2, '0');
const s = String(date.getSeconds()).padStart(2, '0');
const msStr = String(ms).padStart(3, '0');
document.getElementById('clock').textContent = `${h}:${m}:${s}.${msStr}`;
requestAnimationFrame(updateClock);
}
移动端要注意 requestAnimationFrame 在后台标签页会暂停
Chrome、Firefox 等现代浏览器在非活跃标签页中会暂停 requestAnimationFrame,导致时间停止更新。这不是 bug,而是节电机制。如果你需要后台也走时(比如计时器类应用),得 fallback 到 setInterval,但间隔不能低于 1000ms —— 否则仍会被节流。
折中方案是监听页面可见性:
- 用
document.hidden判断是否在后台 - 前台用
requestAnimationFrame;后台降级为setInterval(update, 1000) - 切回前台时,清除
setInterval并立即执行一次update,再启requestAnimationFrame - 注意:iOS Safari 对
visibilitychange事件支持较晚,需检查document.onvisibilitychange是否存在
毫秒显示本质是视觉增强,不是精度需求。真正需要高精度时间同步的场景(如音视频同步),应依赖 performance.now() 和服务器授时,而非本地 Date。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











