最直接有效的方式是用 requestAnimationFrame 统计每秒回调次数:每帧累加计数器,满1000ms计算并重置,采样窗口建议500–1000ms,避免回调中执行耗时操作以确保测量准确。

用 requestAnimationFrame 手动计算 FPS 最直接有效
layui 本身不提供 FPS 监测能力,但它是纯前端框架,完全兼容标准 Web API。最轻量、最可控的方式就是自己用 requestAnimationFrame 统计每秒回调次数——它天然与屏幕刷新率同步,比轮询或定时器更准确。
注意:不要用 setInterval(1000) 去“数帧”,那只是粗略计数,无法反映真实渲染节奏;requestAnimationFrame 的触发时机才真正对应浏览器是否成功提交了一帧。
- 每帧回调时累加计数器
- 每满 1000ms 就算一次 FPS,并重置计时和计数
- 建议采样窗口设为 500–1000ms,太短波动大,太长响应迟钝
- 避免在回调里做耗时操作(如 DOM 查询、console.log),否则会污染测量结果
let fpsCount = 0;
let lastFpsTime = performance.now();
let currentFps = 60;
<p>function tick() {
fpsCount++;
const now = performance.now();
if (now >= lastFpsTime + 1000) {
currentFps = Math.round((fpsCount * 1000) / (now - lastFpsTime));
fpsCount = 0;
lastFpsTime = now;
// 这里可触发 layui 的提示或状态更新
if (currentFps < 45) {
console.warn('FPS 偏低:', currentFps);
// 例如:layui.layer.msg(<code>卡顿预警: ${currentFps}fps</code>, {skin: 'layui-bg-red'});
}
}
requestAnimationFrame(tick);
}
requestAnimationFrame(tick); // 启动
别把 FPS 当唯一指标,要结合 performance.getEntriesByType('longtask')
只看 FPS 容易误判。比如页面静止时 FPS 恒为 0,但并不卡;又或者 FPS 看似 58,实际有大量 80ms 的长任务堆积在主线程,用户一交互就卡住——这种“假流畅”在 layui 表单联动、树形组件展开等场景特别常见。
必须同时监听长任务:PerformanceObserver 能捕获所有 >50ms 的主线程阻塞,这才是导致“点击无响应”“下拉卡半秒”的元凶。
立即学习“前端免费学习笔记(深入)”;
前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。
- 启用前确保浏览器支持(Chrome 58+、Edge 79+、Safari 15.4+)
- 不要在
requestAnimationFrame回调里调用console.log或触发 layui UI 更新,否则可能制造新的长任务 - 可配合
performance.now()记录任务开始/结束时间,定位是哪个 layui 方法(如layui.form.render())拖慢了主线程
const longTaskObserver = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.duration > 80) { // 超过 3 帧就值得警惕
console.warn('长任务:', entry.startTime, '持续', entry.duration.toFixed(1), 'ms');
// 这里可上报或标记当前页面为“高风险卡顿态”
}
});
});
longTaskObserver.observe({ entryTypes: ['longtask'] });
layui 场景下容易被忽略的卡顿诱因
很多卡顿不是代码慢,而是 layui 的默认行为在特定条件下被放大。比如:
-
layui.table.render()渲染上千行数据时,默认开启height: 'full-xx'会触发频繁 resize 和重布局,比单纯渲染慢 3–5 倍 -
layui.tree展开深层嵌套节点时,若每个节点含复杂模板(如带图标 + badge + 按钮),CSS 选择器重计算成本陡增 -
layui.form.on('select(xxx)')回调里执行$.ajax并同步更新另一个 select,容易引发强制同步布局(Layout Thrashing) - 使用
layui.upload上传大文件时,若在done回调里直接layui.table.reload(),会触发整表重绘而非增量更新
这些情况即使 FPS 显示 55–58,用户也会明显感知到“粘滞感”。建议在关键交互后手动打点,比如在 table.reload 完成后 100ms 内检查 performance.now() 差值,判断是否真正在“视觉响应”层面卡住。
线上环境慎用实时 FPS 显示,优先走 silent 上报
开发时用 console 或浮动层看 FPS 没问题,但上线后如果每个用户都在跑 requestAnimationFrame + 频繁 DOM 更新(比如用 layui.element.progress() 动态刷 FPS 数字),反而会成为新瓶颈。
真实项目中更推荐:只在检测到连续 3 秒 currentFps 且存在 <code>longtask 时,才采集堆栈快照(new Error().stack)、当前路由、layui 版本、设备像素比,并异步上报。
- 避免用
JSON.stringify(performance.memory),Safari 不支持,且内存数据对卡顿归因价值低 - 上报字段至少包含:
fps_min_3s、longtask_count_3s、url、userAgent、timestamp - 不要在
requestAnimationFrame里调用fetch,改用navigator.sendBeacon防止丢失
卡顿从来不是单一数字能定义的。FPS 是表,长任务是里,而 layui 的组件生命周期钩子(如 done、success、render)才是你真正该埋点的地方。

















