如何在React中使用Performance API测量状态变更后的显示更新耗时及mark/measure放置
使用Performance API测量React状态变更后的渲染耗时
核心逻辑
React状态更新后的渲染分两步:先完成DOM同步更新(React的commit阶段),再由浏览器完成重绘/重排让用户看到最终效果。要精准统计总耗时,得把标记点放在状态变更触发时和浏览器完成重绘后。
具体实现步骤
1. 打起始标记
在触发状态变更的代码前,用performance.mark()标记开始时间:
const handleTriggerStateChange = () => { // 标记状态变更启动时刻 performance.mark('state-update-start'); // 触发状态变更 setSomeState(prev => /* 状态更新逻辑 */); };
2. 标记渲染完成的结束点
结合useLayoutEffect和requestAnimationFrame,确保捕获到浏览器完成重绘的时刻:
import { useState, useLayoutEffect } from 'react'; function TargetComponent() { const [someState, setSomeState] = useState(initialValue); useLayoutEffect(() => { // useLayoutEffect在DOM更新后同步执行,此时DOM已变更但浏览器未重绘 // 用requestAnimationFrame等待下一帧,确保浏览器完成重绘 const frameId = requestAnimationFrame(() => { performance.mark('render-complete-end'); // 计算并获取耗时 performance.measure( 'state-to-render-duration', 'state-update-start', 'render-complete-end' ); const measureResult = performance.getEntriesByName('state-to-render-duration')[0]; console.log(`状态更新到显示完成总耗时:${measureResult.duration}ms`); // 清理标记和测量数据,避免内存冗余 performance.clearMarks('state-update-start'); performance.clearMarks('render-complete-end'); performance.clearMeasures('state-to-render-duration'); }); return () => cancelAnimationFrame(frameId); }, [someState]); // 依赖目标状态,状态变更时自动触发 // 组件其他渲染逻辑... }
3. 标记位置的原因
- 起始标记:放在状态更新前,确保统计的是从状态变更触发到最终显示的完整流程。
- useLayoutEffect:React commit阶段结束后立即执行,是React层面能确认DOM更新完成的最早时机。
- requestAnimationFrame:回调会在浏览器下一次重绘前执行,执行完毕后浏览器就会把更新后的DOM渲染到屏幕,此时标记结束点,能准确反映用户看到更新的时间。
注意事项
- 如果状态更新依赖异步操作(比如API请求),把起始标记放在异步回调、即将触发状态更新的位置。
- 多次测量时必须清理旧标记,避免
performance.getEntriesByName返回大量历史数据。 - 生产环境可通过环境变量包裹统计逻辑,避免性能损耗:
if (process.env.NODE_ENV === 'development') { // Performance API相关代码 }
内容的提问来源于stack exchange,提问作者claya
相关产品推荐
相关产品推荐

