如何在Preact中测量包含光栅化阶段的组件渲染耗时?
如何在Preact中测量包含光栅化阶段的完整组件更新渲染耗时?
问题描述
我想要统计Preact组件更新与渲染的完整耗时,目前我写了如下代码来测量:
handleKeyPress(event) { renderingTime = Date.now() // ... 其他逻辑 } componentDidUpdate() { if (renderingTime) { console.log('finished rendering ' + (Date.now() - renderingTime)) this.onNextFrame(function() { console.log('finished rendering anim ' + (Date.now() - renderingTime)) }) } } onNextFrame(callback) { setTimeout(function () { window.requestAnimationFrame(callback) }, 0) }
我在类Chromium的Cobalt浏览器中获取了性能追踪结果,发现:
finished rendering日志仅统计了JavaScript执行耗时finished rendering anim日志统计了JavaScript执行+布局耗时
但追踪结果显示media_pipeline的光栅化阶段在这之后仍在进行,我希望能把光栅化阶段的耗时也纳入测量,并且输出到控制台用于自动化测试,请问有什么办法吗?
解决方案
针对你的需求,结合Cobalt基于Chromium的特性,有两种可靠的方案可以将光栅化耗时纳入测量,以下是具体实现:
方案1:利用Performance API精准捕获光栅化完成事件
Chromium内核的浏览器会通过Performance API暴露渲染流水线的各个阶段事件,其中paint类型的性能条目对应光栅化完成的节点。我们可以用PerformanceObserver监听这个事件,结合性能标记来计算完整耗时:
调整后的代码
// 类组件中添加相关逻辑 handleKeyPress(event) { // 创建性能标记,替代Date.now(),精度更高 performance.mark('component-update-start'); // ... 你的其他按键处理逻辑 } componentDidMount() { // 初始化性能观察者,监听paint事件 this.paintObserver = new PerformanceObserver((entryList) => { const paintEntries = entryList.getEntries(); // 获取之前创建的开始标记 const startMark = performance.getEntriesByName('component-update-start')[0]; if (startMark) { // 计算从组件更新开始到光栅化完成的总耗时 // paint条目startTime是事件触发时间,duration是光栅化耗时 const totalFullRenderTime = (paintEntries[paintEntries.length - 1].startTime + paintEntries[paintEntries.length - 1].duration) - startMark.startTime; console.log('finished full rendering (JS + layout + rasterization):', totalFullRenderTime.toFixed(2) + 'ms'); // 清理标记,避免重复统计 performance.clearMarks('component-update-start'); // 单次统计完成后断开观察者,避免内存泄漏 this.paintObserver.disconnect(); } }); // 配置观察者监听paint类型的性能条目 this.paintObserver.observe({ entryTypes: ['paint'] }); } componentDidUpdate() { const startMark = performance.getEntriesByName('component-update-start')[0]; if (startMark) { // 原有的JS执行耗时统计 console.log('finished rendering (JS only):', (Date.now() - startMark.startTime).toFixed(2) + 'ms'); this.onNextFrame(() => { // 原有的JS+布局耗时统计 console.log('finished rendering (JS + layout):', (Date.now() - startMark.startTime).toFixed(2) + 'ms'); }); } } componentWillUnmount() { // 组件卸载时清理观察者,防止内存泄漏 if (this.paintObserver) { this.paintObserver.disconnect(); } } onNextFrame(callback) { setTimeout(() => window.requestAnimationFrame(callback), 0); }
方案2:用requestIdleCallback等待浏览器完成所有渲染任务
如果Cobalt对Performance Observer的支持有限,你可以用requestIdleCallback来实现——这个API会在浏览器完成所有紧急渲染任务(包括光栅化)后触发,适合作为光栅化完成的标记:
调整后的componentDidUpdate方法
componentDidUpdate() { if (renderingTime) { console.log('finished rendering ' + (Date.now() - renderingTime) + 'ms'); this.onNextFrame(() => { console.log('finished rendering anim ' + (Date.now() - renderingTime) + 'ms'); // 等待浏览器空闲,此时光栅化已完成 requestIdleCallback(() => { console.log('finished full rendering (including rasterization):', (Date.now() - renderingTime) + 'ms'); }); }); } }
注意:
requestIdleCallback的触发时机受浏览器负载影响,如果需要精准的性能数据,优先选择方案1。
自动化测试适配建议
为了方便自动化测试框架捕获和解析性能数据,建议输出结构化的日志:
// 输出结构化JSON格式日志,便于测试工具提取 console.log('performance-metric', JSON.stringify({ metricName: 'full-render-duration', valueMs: totalFullRenderTime, timestamp: Date.now() }));
内容的提问来源于stack exchange,提问作者julien H.
相关产品推荐
相关产品推荐

