Next.js の reportWebVitals を調べてみる
概要
Next.js の reportWebVitals()
について調べたことをまとめるスクラップです。
参照
reportWebVitals() とは
基本的には、Vercel が提供する Next.js Analytics というサービスで使用される関数です。
Vercel にデプロイしている場合は、設定無し( 関数の記述も無し )でエクスペリエンススコア( Experience Score ) の取集ができますが、設定すればセルフホスティングしているサービスにも Next.js Analytics を使用できるみたいです。[1]
また送信先を変更すれば、他のWebサービスなどとの連携も可能なようです。[2]
Next.jsを使ってパフォーマンス解析などをしたい場合は、他のライブラリなどを読み込まなくても使用できるため、候補として挙げても良い感じがします👀
使用方法
カスタム App ( pages/_app.[jsx|tsx]
のこと ) 内で reportWebVitals()
を export するだけで使用できます👇
import type { AppProps, NextWebVitalsMetric } from 'next/app'
// メトリクス を取得する
export function reportWebVitals(metric: NextWebVitalsMetric) {
console.log(metric)
}
function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />
}
export default MyApp
取得できる値
TypeScriptの型を分かりやすくしたら、以下のような感じ👇
type NextWebVitalsMetric =
| {
id: string;
startTime: number;
value: number;
label: "web-vital";
name: "FCP" | "LCP" | "CLS" | "FID" | "TTFB";
}
| {
id: string;
startTime: number;
value: number;
label: "custom";
name:
| "Next.js-hydration"
| "Next.js-route-change-to-render"
| "Next.js-render";
};
それぞれのプロパティについて
-
id
: 現在のページのメトリクスに紐づいた一意な値 -
name
: メトリクス名 -
startTime
: 対象のメトリクスが最初に記録された時間( ミリ秒 ) -
value
: メトリクスの値。メトリクスの種別によって値は変わる- 例:
"TTFB"
の場合は72
,"FCP"
の場合は44918.30000000447
などのようになる
- 例:
-
label
: メトリクスの種別 (web-vital
またはcustom
)
WebVitals について
Web Vitals は、主に Google が提唱している Web パフォーマンス( ユーザーエクスペリエンス )の指標です。
具体的な要素として以下のようなものがあります👇
- 最初の1バイトを受信するまでの時間 / Time to First Byte (TTFB)
- 最初の DOM をレンダリングするまでの時間 / First Contentful Paint (FCP)
- 表示される最も大きい要素のレンダリング時間 / Largest Contentful Paint (LCP)
- ユーザー操作をブラウザが処理を開始するまでの時間 / First Input Delay (FID)
- 予期しないレイアウトシフトの最大値 / Cumulative Layout Shift (CLS)
※ レイアウトシフト: 表示内容が予期しない時に変更されること ( 例: 広告などによって表示がズレるなど )
reportWebVitals()
では、上記の情報を label
を使って簡単に取得することができます👇
export function reportWebVitals(metric: NextWebVitalsMetric) {
if( metric.label === "web-vital" ) {
// WebVitals のスコアを使った処理
}
}
または、以下のように switch 文を使用することで、それぞれのメトリクスにアクセスできます👇
export function reportWebVitals(metric: NextWebVitalsMetric) {
switch (metric.name) {
case 'FCP':
// handle FCP results
break
case 'LCP':
// handle LCP results
break
case 'CLS':
// handle CLS results
break
case 'FID':
// handle FID results
break
case 'TTFB':
// handle TTFB results
break
default:
break
}
}
カスタムメトリクス( Custom Metrics ) について
Next.js では、WebVitals に加えて、ページがハイドレイト( hydrate )してレンダリングされるのにかかる時間を測定するいくつかの追加のカスタムメトリクスがあります。
具体的な要素は以下の通りです👇
-
Next.js-hydration
:ページのハイドレイト( hydrate )の開始と終了にかかる時間 ( ミリ秒単位 ) -
Next.js-route-change-to-render
:ルート変更後にページがレンダリングを開始するのにかかる時間 ( ミリ秒単位 ) -
Next.js-render
:ルート変更後にページのレンダリングが完了するまでにかかる時間 ( ミリ秒単位 )
※ ハイドレイト( hydrate/hydration ): コンポーネントをレンダリングしてイベントハンドラーをアタッチするプロセスは、「ハイドレーション」と呼ばれます。( こちらのissue より引用 )
reportWebVitals()
では、上記の情報を label
を使って簡単に取得することができます👇
export function reportWebVitals(metric: NextWebVitalsMetric) {
if (metric.label === 'custom') {
// Custom Metrics のスコアを使った処理
}
}
もちろん、name
を使って、それぞれのメトリクスにアクセスすることもできます👇
export function reportWebVitals(metric: NextWebVitalsMetric) {
switch (metric.name) {
case 'Next.js-hydration':
// handle hydration results
break
case 'Next.js-route-change-to-render':
// handle route-change to render results
break
case 'Next.js-render':
// handle render results
break
default:
break
}
}
他のサービスなどにメトリクスを送信する
relay 機能として使うと、任意の結果を分析エンドポイントに送信して、サイトの実際のユーザーパフォーマンスを測定や追跡できます👇
export function reportWebVitals(metric) {
const body = JSON.stringify(metric)
const url = 'https://example.com/analytics'
// `navigator.sendBeacon()` が使用可能な場合はそちらを使用し、ダメな場合は `fetch()` で代用します
if (navigator.sendBeacon) {
navigator.sendBeacon(url, body)
} else {
fetch(url, { body, method: 'POST', keepalive: true })
}
}
※ navigator.sendBeacon()
: Navigator.sendBeacon() - Web API | MDN を参照
参照
Google Analytics との連携については、以下のリポジトリが参考になります👇
Google Analyticsにメトリクスを送信する
以下のソースコードは、Google Analytics に WebVitals の値を送信する例です。
NEXT_PUBLIC_GA_TRACKING_ID
には、Google Analytics のダッシュボードで取得できる Traking ID ( UA-****
みたいなヤツ ) を指定してください。以下の例では、.env.local
ファイルに環境変数として定義しています。
import type { AppProps, NextWebVitalsMetric } from "next/app";
import "../styles/globals.css";
// Google Analytics に WebVitals を送信する
export function reportWebVitals(metric: NextWebVitalsMetric) {
const { id, name, value, label } = metric;
window.gtag("event", name, {
event_label: id,
event_action: name,
non_interaction: true,
value: Math.round(name === "CLS" ? value * 1000 : value),
event_category: label === "web-vital" ? "Web Vitals" : "Next.js custom metric",
});
}
export default function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} />;
}
import Document, { Html, Head, Main, NextScript } from "next/document";
const GA_TRACKING_ID = process.env.NEXT_PUBLIC_GA_TRACKING_ID || "";
export default class MyDocument extends Document {
render() {
return (
<Html>
<Head>
{/* Global Site Tag (gtag.js) - Google Analytics */}
<script
async
defer
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
<script
id="gtag-init"
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
送信した値をグラフ化する
Web Vitals Reportのリポジトリより引用
上記のツールを使うことで表示できるようです。( 私の場合だと、エラーが発生して確認できませんでした... )