使用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
相关产品推荐
相关产品推荐

