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

使用Puppeteer/无头Chrome上报性能指标问题咨询

解决Puppeteer获取首绘(First Paint)时间的问题

嘿,我来帮你搞定这个问题——你遇到的Performance.metrics事件没触发的情况,主要是因为这个事件的触发时机比较苛刻,而且有些指标(比如首绘)只会在页面加载早期生成一次,很可能在你注册监听之前就已经完成了。下面给你两种稳定的解决方案:

方案一:主动调用CDP接口获取性能指标

这种方法不需要依赖事件监听,直接主动请求所有已收集的性能数据,稳定性拉满:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  // 别用page._client了,这是内部私有属性,用官方推荐的createCDPSession创建会话
  const cdpSession = await page.target().createCDPSession();

  // 启用必要的DevTools域
  await cdpSession.send('Page.enable');
  await cdpSession.send('Performance.enable');

  // 等待页面加载完成
  await page.goto('http://example.com', { waitUntil: 'networkidle2' });

  // 主动拉取所有性能指标
  const { metrics } = await cdpSession.send('Performance.getMetrics');
  
  // 筛选出首绘时间(单位是毫秒)
  const firstPaintMetric = metrics.find(metric => metric.name === 'FirstPaint');
  console.log(`首绘时间:${firstPaintMetric.value}ms`);

  await browser.close();
})();

重点提醒:

  • 放弃page._client,改用page.target().createCDPSession(),这是Puppeteer官方支持的CDP会话创建方式,不会因为版本更新突然失效。
  • Performance.getMetrics会返回所有Chrome收集的性能指标,除了FirstPaint,你还能找到FirstContentfulPaint(首次内容绘制)、DomContentLoaded等其他有用指标。

方案二:在页面内用PerformanceObserver精准捕获首绘

这种方法模拟浏览器原生的性能监听逻辑,能在首绘发生的第一时间捕获数据,连更早的时机都不会错过:

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();

  // 必须在页面加载前注入监听代码,不然会错过首绘
  await page.evaluateOnNewDocument(() => {
    window.paintMetricsPromise = new Promise(resolve => {
      const observer = new PerformanceObserver((entryList) => {
        const paintEntries = entryList.getEntriesByType('paint');
        // 整理首绘和首次内容绘制的数据
        const result = {
          firstPaint: paintEntries.find(e => e.name === 'first-paint')?.startTime,
          firstContentfulPaint: paintEntries.find(e => e.name === 'first-contentful-paint')?.startTime
        };
        resolve(result);
        observer.disconnect(); // 拿到数据就停止监听,避免冗余
      });
      observer.observe({ entryTypes: ['paint'] });
    });
  });

  // 加载页面
  await page.goto('http://example.com', { waitUntil: 'networkidle2' });

  // 获取之前注入的监听结果
  const paintMetrics = await page.evaluate(() => window.paintMetricsPromise);
  console.log(`首绘时间:${paintMetrics.firstPaint}ms`);
  console.log(`首次内容绘制时间:${paintMetrics.firstContentfulPaint}ms`);

  await browser.close();
})();

为什么用这种方法?

  • evaluateOnNewDocument会把代码注入到页面的JavaScript执行环境最早期,确保不会错过首绘(首绘发生在页面加载的极早期,普通的page.evaluate可能赶不上)。
  • 除了首绘,还能同时获取首次内容绘制(FCP),这是更能反映用户实际感知的性能指标。

聊聊你原代码的问题

你的原代码里,Performance.metrics事件只有当性能指标更新时才会推送,但首绘是一次性的指标,很可能在你注册监听之前就已经生成了,所以自然收不到事件。另外,部分Chrome版本中,这个事件需要显式指定监听的指标类型,但主动拉取指标显然比依赖事件监听更可靠。

内容的提问来源于stack exchange,提问作者dbslone

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:07:45