You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:39:08