为什么 mrdoob/stats.js 能精准监控性能?
核心在于浏览器提供的 High Resolution Time API 和 V8 Memory API。
// 1. 实例化 & 配置 var stats = new Stats(); stats.showPanel( 0 ); // 0: fps, 1: ms, 2: mb // 2. 添加到 DOM document.body.appendChild( stats.dom ); // 3. 注入渲染循环 function animate() { stats.begin(); // ... 你的渲染代码 (renderer.render) ... stats.end(); requestAnimationFrame( animate ); } requestAnimationFrame( animate );
原理:High Resolution Time API
为什么准? Stats.js 不使用 Date.now(),而是使用 performance.now()。
Date.now() 是整数毫秒,而 performance.now() 返回浮点数(如 16.695ms),精确到微秒级。// 1. stats.begin() 内部: // 获取高精度时间戳 (例如: 2500.1200000047684) var beginTime = ( performance || Date ).now(); // ... 执行你的渲染代码 ... // 2. stats.end() 内部: var time = ( performance || Date ).now(); // 精确计算差值,甚至能捕捉到 0.1ms 的波动 var ms = time - beginTime;
原理:Chrome V8 非标准扩展 API
数据从哪来? 这是一个 Chrome/Chromium 特有的私有 API performance.memory。Firefox 和 Safari 出于安全隐私考虑(防止指纹追踪)默认不提供此数据。
// 浏览器控制台输入 performance.memory 查看:
{
jsHeapSizeLimit: 4294705152, // ~4GB 上限
totalJSHeapSize: 23545125, // 已申请
usedJSHeapSize: 18454321 // 实际使用 <--- Stats.js 监控此值
}
简单地计算 `1 / delta` 会导致数值剧烈跳动。Stats.js 采用"一秒一结"的策略:累加这一秒内执行了多少次 `end()`,然后刷新显示。
frames++;
if (time > prevTime + 1000) {
// 这一秒内渲染了多少帧?
fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) );
prevTime = time;
frames = 0;
}
完全基于左侧原理手写的 MiniStats
JavaScript 运行在浏览器的主线程上。如果我们使用 while 循环强行占用 CPU,浏览器就无法去处理绘制下一帧的任务,从而产生“卡顿”。
stats.end() 测得的时间 = 渲染耗时 + 40ms 阻塞时间。MS 面板数值直接飙升。