网站首页 > 技术文章 正文
大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发,您的支持是我不断创作的动力。
1. 为什么需要 Performance API
Performance API 能保证达到亚毫秒级(小于 1ms)分辨率的时间和稳定的单调时钟,同时 不受系统时钟偏差或调整的影响。
// Performance interface
[Exposed=(Window,Worker)]
interface Performance : EventTarget {
DOMHighResTimeStamp now();
readonly attribute DOMHighResTimeStamp timeOrigin;
[Default] object toJSON();
};
Performance API 通过 DOMHighResTimeStamp 类型来实现高精度计时,其单位为毫秒,精度为 5 us(微秒),用双精度类型 (double) 存储。但如果浏览器由于硬件、软件限制或安全、隐私等无法提供精确到 5 微秒的时间时,则可以将该值表示为精确到毫秒的时间值。
DOMHighResTimeStamp 可用于描述离散的时间点或时间间隔,而起始时间可以是网站或应用脚本确定的具体时间,也可以是时间原点,即 timeOrigin。
2. Performance API 中的 timeOrigin 如何界定
Performance API 使用 Performance.timeOrigin 来确定与性能相关的时间戳的基准,所有 DOMHighResTimeStamp 时间都相对于 timeOrigin 属性。
在 Window 上下文中,该时间是导航开始的时间,而在 Worker 和 ServiceWorker 上下文中则表示 Worker 运行的时间。
// Level 1 标准会有时钟变化的风险
currentTime = performance.timing.navigationStart + performance.now();
// Level 2 避免了时钟变化的风险
currentTime = performance.timeOrigin + performance.now();
timeOrigin 经过了两个核心阶段,即 Level 1 和 Level 2 规范:
- Level 1 规范:performance.now() 是相对于导航计时规范中的 performance.timing.navigationStart 属性
- Level 2 规范:performance.now() 相对于 Performance.timeOrigin,从而避免了跨网页比较时间戳时时钟变化的风险
同时,为了解决 Window 和 Worker 上下文中不同的时间来源差异,开发者可以借助 performance.timeOrigin 转换来自 Worker 脚本的时间戳以便应用程序时间保持同步。
// worker.js
self.addEventListener("connect", (event) => {
const port = event.ports[0];
port.onmessage = function (event) {
const workerTaskStart = performance.now();
// doSomeWork()
const workerTaskEnd = performance.now();
};
// 将特定 worker 的时间戳转化为绝对时间,同时通知到主页面
// performance.timeOrigin 表示 worker 运行的时间
// performace.now 都是相对于 performance.timeOrigin 计算
port.postMessage({
startTime: workerTaskStart + performance.timeOrigin,
endTime: workerTaskEnd + performance.timeOrigin,
});
});
下面是 main.js 的内容:
const worker = new SharedWorker("worker.js");
worker.port.addEventListener("message", (event) => {
// 将绝对时间转化为相对于 window 的时间戳
const workerTaskStart = event.data.startTime - performance.timeOrigin;
const workerTaskEnd = event.data.endTime - performance.timeOrigin;
console.log("Worker task start:", workerTaskStart);
console.log("Worker task end:", workerTaskEnd);
});
需要注意的是,performance.timeOrigin 的值可能与在时间原点执行的 Date.now() 返回的值不同,因为 Date.now() 可能受到系统和用户时钟调整、时钟偏差等的影响,而 timeOrigin 属性是一个单调时钟,当前时间永远不会减少,也不受各种调整的影响。
3.Performance.now() 与 Date.now() 有什么不同
Date.now() 表示为自纪元以来经过的毫秒数,纪元为 1970 年 1 月 1 日凌晨 12 点,表示 UTC 时间,而 performance.now() 方法则与 Performance.timeOrigin 相关。
Date.now();
// 输出 1678889977578
performance.now();
// 输出 233936
JavaScript 时间会受到系统时钟偏差或调整的影响,即时间值始终单调递增的假设会被打破。Date 对象的主要用途是向用户显示时间和日期信息,因此操作系统会运行一个守护进程来定期同步时间,即时钟可能会每小时调整几次,而每次调整几毫秒。
相比之下,performance.now() 提供单调递增的时间值且不受时钟调整的影响。这意味着可以保证 DOMHighResTimeStamp 值至少等于上次访问时的值。
因此为了测量性能、计算精确的帧速率(FPS)、动画循环等,建议开发者使用 Performance.now() 而非 Date.now()。
4. 如何避免 Performance API 精度降低
为了防范时序攻击 (Timing Attacks) 和指纹识别 (Fingerprinting),DOMHighResTimeStamp 类型会考虑站点隔离 (Site Isolation) 状态:
- 隔离环境下的分辨率:5us
- 非隔离环境下的分辨率:100us
开发者可以使用
Cross-Origin-Opener-Policy (COOP) 和
Cross-Origin-Embedder-Policy (COEP) 标头来实现跨域隔离,例如:
Cross-Origin-Opener-Policy: same-origin
// 同源
Cross-Origin-Embedder-Policy: require-corp
以上设置可以确保顶级文档不会与跨域文档共享浏览上下文组 (Browsing Context Group),此时潜在攻击者在弹出窗口中打开文档时将无法访问全局对象,从而阻止了一系列被称为 XS-Leaks 的跨域攻击。
参考资料
https://developer.mozilla.org/en-US/docs/Web/API/DOMHighResTimeStamp
https://developer.mozilla.org/en-US/docs/Web/API/Performance/timeOrigin
https://w3c.github.io/hr-time/#dom-domhighrestimestamp
https://medium.com/@vinaychhabra.dev/exploring-javascript-performance-measurement-with-performance-now-d79cb39dc269
猜你喜欢
- 2025-07-06 实战分享:邀请有礼产品怎么做?(邀请有礼活动)
- 2025-07-06 揭秘:视频播放网站如何精准记录用户观看进度
- 2025-07-06 第七篇 前端基础十问,看看你是否真的掌握扎实?
- 2025-07-06 下单预约送货时间功能设计及思路(预约送货怎么说)
- 2025-07-06 每天一个 Python 库:datetime 模块全攻略,时间操作太丝滑!
- 2025-07-06 前端代码更新,如何优雅地通知用户刷新页面?
- 2024-10-09 JS Date对象的妙用:如何更优雅的计算时间差?
- 2024-10-09 从零开始的前端请求之旅 从零开始学前端开发
- 2024-10-09 如何在上班时间利用终端控制台摸鱼??????
- 2024-10-09 JS中setTimeout和setInterval 实现倒计时如何解决时间偏差的问题
你 发表评论:
欢迎- 593℃几个Oracle空值处理函数 oracle处理null值的函数
- 586℃Oracle分析函数之Lag和Lead()使用
- 574℃0497-如何将Kerberos的CDH6.1从Oracle JDK 1.8迁移至OpenJDK 1.8
- 571℃Oracle数据库的单、多行函数 oracle执行多个sql语句
- 567℃Oracle 12c PDB迁移(一) oracle迁移到oceanbase
- 559℃【数据统计分析】详解Oracle分组函数之CUBE
- 546℃最佳实践 | 提效 47 倍,制造业生产 Oracle 迁移替换
- 540℃Oracle有哪些常见的函数? oracle中常用的函数
- 最近发表
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端react (48)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- 前端懒加载 (49)
- 前端获取当前时间 (50)
- Oracle RAC (73)
- oracle恢复 (76)
- oracle 删除表 (48)
- oracle 用户名 (74)
- oracle 工具 (55)
- oracle 内存 (50)
- oracle 导出表 (57)
- oracle 中文 (51)
- oracle的函数 (57)
- 前端调试 (52)
本文暂时没有评论,来添加一个吧(●'◡'●)