puppeteer

Puppeteer で FirstMeaningfulPaint を取得する

概要

  • パフォーマンスメトリクスのAPIを有効化する
  • setTimeout ループで FMP取得まで待つ

コード

npm install -S puppeteer

fmp.js
const puppeteer = require('puppeteer')

async function getPerformanceMetrics(page) {
  const { metrics } = await page._client.send('Performance.getMetrics')
  return metrics.reduce((acc, i) => ({ ...acc, [i.name]: i.value }), {})
}

async function waitForFMP(page) {
  let doneMet = null
  while (true) {
    const data = await getPerformanceMetrics(page)
    if (data.FirstMeaningfulPaint !== 0) {
      doneMet = data
      break
    }
    await new Promise(resolve => setTimeout(resolve, 300))
  }
  return doneMet
}

async function run(url) {
  const browser = await puppeteer.launch()
  const page = await browser.newPage()
  await page._client.send('Performance.enable')
  await page.goto(url)
  const m = await waitForFMP(page)
  console.log(
    `${url}: ${~~((m.FirstMeaningfulPaint - m.NavigationStart) * 1000)}ms`
  )
  browser.close()
}
;(async () => {
  await run('https://google.com')
  await run('https://qiita.com')
  await run('https://github.com')
  await run('https://twitter.com/mizchi')
  await run('https://r.nikkei.com')
  await run('https://yahoo.co.jp')
  await run('https://www.rakuten.co.jp/')
})()

ターゲットはそれっぽいサイトを適当に選びました。

$ node fmp.js
https://google.com: 669ms
https://qiita.com: 723ms
https://github.com: 1089ms
https://twitter.com/mizchi: 992ms
https://r.nikkei.com: 849ms
https://yahoo.co.jp: 618ms
https://www.rakuten.co.jp/: 452ms

async/await と object-rest-spread 使ってるのでたぶん node 8.x じゃないと動かないです。

これたぶんUnixtime で返ってて Timestamp からの差分だと思うけど自信はない。

おまけ: 取得できる内部パラメータ

今回は getPerformanceMetrics の FirstMeaningfullPaint だけ使いましたが、内部的にはこんなパラメータが取れています。

export type Metrics = {
  Timestamp: number,
  AudioHandlers: number,
  Documents: number,
  Frames: number,
  JSEventListeners: number,
  LayoutObjects: number,
  MediaKeySessions: number,
  MediaKeys: number,
  Nodes: number,
  Resources: number,
  ScriptPromises: number,
  SuspendableObjects: number,
  V8PerContextDatas: number,
  WorkerGlobalScopes: number,
  UACSSResources: number,
  LayoutCount: number,
  RecalcStyleCount: number,
  LayoutDuration: number,
  RecalcStyleDuration: number,
  ScriptDuration: number,
  TaskDuration: number,
  JSHeapUsedSize: number,
  JSHeapTotalSize: number,
  FirstMeaningfulPaint: number,
  DomContentLoaded: number,
  NavigationStart: number
}

TaskDuration がJSの実行やCSSのLayoutにかかった時間の合計で、ここらへんも参考になります。