ユーザー中心の指標を使用することには、どのウェブサイトでも普遍的に測定できる多くの価値があります。これらの指標を使用すると、次のことができます。
- 実際のユーザーがウェブ全体をどのように体験しているかを把握できます。
- 自分のサイトを競合他社のサイトと比較する。
- カスタムコードを記述しなくても、分析ツールで有用で実用的なデータを追跡できます。
ユニバーサル指標は良い基準となりますが、特定のサイトの全体的なエクスペリエンスを把握するには、多くの場合、これら以外の指標を測定する必要があります。
カスタム指標を使用すると、そのサイトのみに当てはまる、サイトの利便性に関する次のような要素を測定できます。
- SPA が 1 つの「ページ」から移行するのにかかる時間必要があります。
- データベースから取得したデータをログイン ユーザーのページに表示するのにかかる時間です。
- サーバーサイド レンダリング(SSR)アプリのハイドレートにかかる時間。
- リピーターによって読み込まれたリソースのキャッシュ ヒット率。
- ゲーム内のクリックやキーボード イベントのイベント レイテンシ。
カスタム指標を測定するための API
これまでウェブ デベロッパーは、パフォーマンスを測定するための低レベル API をあまり用意していなかったため、サイトのパフォーマンスを測定するためにハッキングに頼る必要がありました。
たとえば、requestAnimationFrame
ループを実行して各フレーム間の差分を計算することで、長時間実行される JavaScript タスクが原因でメインスレッドがブロックされているかどうかを判定できます。差分がディスプレイのフレームレートよりも大幅に長い場合は、長いタスクとして報告できます。ただし、このようなハッキングは、実際にはパフォーマンスそのもの(バッテリーの消耗など)に影響するため、おすすめしません。
パフォーマンス測定を効果的に行うための第一のルールは、パフォーマンス測定の手法自体がパフォーマンスの問題を引き起こしていないことを確認することです。そのため、サイトで測定するカスタム指標については、次のいずれかの API を使用することをおすすめします。
Performance Observer API
対応ブラウザ
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Performance Observer API は、このページで説明する他のすべての Performance API からデータを収集して表示するメカニズムです。これを理解することは、質の高いデータを得るために不可欠です。
PerformanceObserver
を使用すると、パフォーマンス関連のイベントを受動的にサブスクライブできます。これにより、API コールバックはアイドル期間にも配信されるため、通常はページのパフォーマンスが妨げられることはありません。
PerformanceObserver
を作成するには、新しいパフォーマンス エントリがディスパッチされるたびに実行されるコールバックを渡します。次に、observe()
メソッドを使用して、リッスンするエントリのタイプをオブザーバーに指示します。
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
po.observe({type: 'some-entry-type'});
以下のセクションでは、監視に使用できるさまざまなエントリタイプをすべて示しますが、新しいブラウザでは、静的 PerformanceObserver.supportedEntryTypes
プロパティで使用できるエントリタイプを調べることができます。
すでに発生しているエントリを確認する
デフォルトでは、PerformanceObserver
オブジェクトは発生したエントリのみを監視できます。パフォーマンス分析コードを遅延読み込みして優先度の高いリソースをブロックしないようにすると、問題が発生する可能性があります。
過去のエントリを(発生した後に)取得するには、observe()
を呼び出すときに buffered
フラグを true
に設定します。ブラウザには、初めて PerformanceObserver
コールバックが呼び出されたときに、パフォーマンス エントリ バッファからそのタイプの最大バッファサイズまでの過去のエントリが含まれます。
po.observe({
type: 'some-entry-type',
buffered: true,
});
従来のパフォーマンス API を使用して
Performance Observer API の導入以前は、performance
オブジェクトで定義された次の 3 つのメソッドを使用してパフォーマンス エントリにアクセスできました。
これらの API は引き続きサポートされますが、新しいエントリが出力されたタイミングをリッスンできないため、使用はおすすめしません。また、多くの新しい API(largest-contentful-paint
など)は performance
オブジェクトを介してではなく、PerformanceObserver
を介してのみ公開されます。
Internet Explorer との互換性が特に必要な場合を除き、コード内でこれらのメソッドを使用せず、今後は PerformanceObserver
を使用することをおすすめします。
カスタム速度 API
対応ブラウザ
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
User Timing API は、 測定用の API を使用します必要に応じて任意の点にマークを付けて マークとマークの間の時間を測定します。
// Record the time immediately before running a task.
performance.mark('myTask:start');
await doMyTask();
// Record the time immediately after running a task.
performance.mark('myTask:end');
// Measure the delta between the start and end of the task
performance.measure('myTask', 'myTask:start', 'myTask:end');
Date.now()
や performance.now()
などの API も同様の機能を備えていますが、User Timing API を使用する利点は、パフォーマンス ツールと緊密に統合できることです。たとえば、Chrome DevTools では [パフォーマンス] パネルにカスタム速度の測定値が可視化されます。また、多くの分析プロバイダも自動的に測定を行い、時間データを分析バックエンドに送信します。
カスタム速度の測定値を報告するには、PerformanceObserver を使用して登録し、measure
タイプのエントリを監視します。
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `measure` entries to be dispatched.
po.observe({type: 'measure', buffered: true});
長時間タスク API
対応ブラウザ
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Long Tasks API は、ブラウザのメインスレッドが、フレームレートや入力レイテンシに影響するほど長時間ブロックされているタイミングを知るのに役立ちます。API は、実行時間が 50 ミリ秒を超えていたすべてのタスクを報告します。
<ph type="x-smartling-placeholder">負荷の高いコードを実行したり、大規模なスクリプトを読み込んで実行したりする必要がある場合、そのコードがメインスレッドをブロックしているかどうかを追跡すると便利です。実際、多くの上位レベルの指標(Time to Interactive(TTI)やTotal Blocking Time(TBT)など)が Long Tasks API 自体の上に構築されています。
時間のかかるタスクが発生するタイミングを判断するには、PerformanceObserver を使用して登録し、longtask
タイプのエントリを監視します。
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `longtask` entries to be dispatched.
po.observe({type: 'longtask', buffered: true});
Long Animation Frames API
対応ブラウザ
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Long Animation Frames API は、Long Tasks API の新しいイテレーションで、長いタスクではなく、50 ミリ秒を超える長いフレームに注目します。これにより、アトリビューションの改善や、潜在的な問題を引き起こす遅延の広がりなど、Long Tasks API の欠点が解消されます。
長いフレームが発生するタイミングを判断するには、PerformanceObserver を使用して登録し、long-animation-frame
タイプのエントリを監視します。
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `long-animation-frame` entries to be dispatched.
po.observe({type: 'long-animation-frame', buffered: true});
Element Timing API
対応ブラウザ
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Largest Contentful Paint(LCP)指標は、最も大きな画像またはテキスト ブロックが画面に描画されたタイミングを把握するのに便利ですが、別の要素のレンダリング時間を測定したい場合もあるでしょう。
このような場合は、Element Timing API を使用します。LCP API は Element Timing API の上に構築されており、最も大きいコンテンツの要素を自動的にレポートします。ただし、他の要素に明示的に elementtiming
属性を追加し、element
エントリタイプを監視する PerformanceObserver を登録することで、他の要素もレポートできます。
<img elementtiming="hero-image" />
<p elementtiming="important-paragraph">This is text I care about.</p>
<!-- ... -->
<script>
const po = new PerformanceObserver((entryList) => {
for (const entry of entryList.getEntries()) {
// Log the entry and all associated details.
console.log(entry.toJSON());
}
});
// Start listening for `element` entries to be dispatched.
po.observe({type: 'element', buffered: true});
</script>
Event Timing API
対応ブラウザ
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Interaction to Next Paint(INP)指標は、ページ全体のすべてのクリック、タップ、キーボード操作を監視することで、ページ全体の応答性を評価します。ほとんどの場合、ページの INP は、ユーザーが操作を開始してから、ユーザーの入力の視覚的な結果を示す次のフレームをブラウザが描画するまで、完了に最も時間がかかった操作です。
INP 指標は Event Timing API で使用可能です。この API は、イベントのライフサイクル中に発生する次のようなさまざまなタイムスタンプを公開します。
startTime
: ブラウザがイベントを受信する時刻。processingStart
: ブラウザでイベントのイベント ハンドラの処理を開始できる時刻。processingEnd
: このイベントのイベント ハンドラから開始されたすべての同期コードの実行をブラウザが終了する時刻。duration
: ブラウザがイベントを受信してから、イベント ハンドラから開始されたすべての同期コードの実行を終了し、次のフレームのペイントが可能になるまでの時間(セキュリティ上の理由から 8 ミリ秒に丸め)。
次の例は、これらの値を使用してカスタム測定を作成する方法を示しています。
const po = new PerformanceObserver((entryList) => {
// Get the last interaction observed:
const entries = Array.from(entryList.getEntries()).forEach((entry) => {
// Get various bits of interaction data:
const inputDelay = entry.processingStart - entry.startTime;
const processingTime = entry.processingEnd - entry.processingStart;
const presentationDelay = entry.startTime + entry.duration - entry.processingEnd;
const duration = entry.duration;
const eventType = entry.name;
const target = entry.target || "(not set)"
console.log("----- INTERACTION -----");
console.log(`Input delay (ms): ${inputDelay}`);
console.log(`Event handler processing time (ms): ${processingTime}`);
console.log(`Presentation delay (ms): ${presentationDelay}`);
console.log(`Total event duration (ms): ${duration}`);
console.log(`Event type: ${eventType}`);
console.log(target);
});
});
// A durationThreshold of 16ms is necessary to include more
// interactions, since the default is 104ms. The minimum
// durationThreshold is 16ms.
po.observe({type: 'event', buffered: true, durationThreshold: 16});
Resource Timing API
対応ブラウザ
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Resource Timing API を使用すると、特定のページのリソースがどのように読み込まれたかをデベロッパーが詳しく把握できます。API の名称に反して、この API が提供する情報はタイミング データだけにとどまりません(多くのデータがあります)。その他、次のようなデータにアクセスできます。
initiatorType
: リソースの取得方法(<script>
タグ、<link>
タグ、fetch()
呼び出しなど)。nextHopProtocol
: リソースを取得するために使用されるプロトコル(h2
やquic
など)。encodedBodySize
/decodedBodySize]: エンコード形式またはデコードされた形式のリソースのサイズ(それぞれ)transferSize
: ネットワーク経由で実際に転送されたリソースのサイズ。リソースがキャッシュによって満たされる場合、この値はencodedBodySize
よりもはるかに小さくなり、ゼロになる場合もあります(キャッシュの再検証が不要な場合)。
リソースのタイミング エントリのtransferSize
プロパティを使用して、キャッシュ ヒット率指標やキャッシュされたリソースの合計サイズ指標を測定できます。これは、リソース キャッシュ戦略がリピーターのパフォーマンスにどのように影響するかを把握するのに役立ちます。
次の例では、ページによってリクエストされたすべてのリソースがログに記録され、各リソースがキャッシュによって満たされたかどうかを示します。
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log(entry.name, entry.transferSize === 0);
}
});
// Start listening for `resource` entries to be dispatched.
po.observe({type: 'resource', buffered: true});
Navigation Timing API
対応ブラウザ
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Navigation Timing API は Resource Timing API に似ていますが、ナビゲーション リクエストのみをレポートします。navigation
エントリタイプも resource
エントリタイプに似ていますが、ナビゲーション リクエストのみに固有の追加情報(DOMContentLoaded
イベントと load
イベントが発生したタイミングなど)が含まれています。
多くのデベロッパーがサーバーの応答時間(Time to First Byte(TTFB))を理解するために追跡している指標の一つは、Navigation Timing API、具体的にはエントリの responseStart
タイムスタンプです。
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// If transferSize is 0, the resource was fulfilled using the cache.
console.log('Time to first byte', entry.responseStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
Service Worker を使用する開発者が考慮すべきもう一つの指標は、ナビゲーション リクエストの Service Worker の起動時間です。これは、ブラウザが Service Worker スレッドを開始してフェッチ イベントのインターセプトを開始するまでの時間です。
特定のナビゲーション リクエストの Service Worker の起動時間は、entry.responseStart
と entry.workerStart
の差分から特定できます。
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Service Worker startup time:',
entry.responseStart - entry.workerStart);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});
Server Timing API
対応ブラウザ
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
-
<ph type="x-smartling-placeholder">
Server Timing API を使用すると、サーバーからブラウザにリクエスト固有のタイミング データをレスポンス ヘッダーを介して渡すことができます。たとえば、特定のリクエストについてデータベース内のデータを検索するのにかかった時間を指定できます。これは、サーバー速度の低下が原因となっているパフォーマンスの問題をデバッグする際に便利です。
サードパーティの分析プロバイダーを使用するデベロッパーにとって、Server Timing API は、サーバーのパフォーマンス データと、これらの分析ツールが測定する可能性のある他のビジネス指標を関連付ける唯一の方法です。
レスポンスでサーバーの時間データを指定するには、Server-Timing
レスポンス ヘッダーを使用します。次に例を示します。
HTTP/1.1 200 OK
Server-Timing: miss, db;dur=53, app;dur=47.2
これで、ページから、Resource Timing API と Navigation Timing API の resource
エントリと navigation
エントリの両方について、このデータを読み取ることができます。
// Create the performance observer.
const po = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// Logs all server timing data for this response
console.log('Server Timing', entry.serverTiming);
}
});
// Start listening for `navigation` entries to be dispatched.
po.observe({type: 'navigation', buffered: true});