JavaScriptのパフォーマンスを測定する方法
JavaScriptのパフォーマンス問題は体感しやすいものの、特定するのは困難です。アプリケーションの動作が重く感じられ、インタラクションに遅延が生じ、ユーザーも気づきます。しかし、適切な測定ツールがなければ、推測に頼るしかありません。本記事では、最新のブラウザAPIとDevToolsを使用した実践的なJavaScriptパフォーマンス測定技術を取り上げ、実際のボトルネックを発見し、適切な箇所を修正できるようにします。
重要なポイント
- 合成(ラボ)テストとリアルユーザーモニタリング(RUM)を区別する — 診断にはラボツールを使用し、検証にはフィールドデータを使用します。
- Chrome DevToolsのPerformanceパネルは、ロングタスク、フレームチャート、コールツリーを表示し、実践的なプロファイリングを可能にします。
- Performance API(
performance.now()、performance.mark()、performance.measure())は、DevToolsと統合された正確でプログラマティックなタイミング測定を提供します。 PerformanceObserverは、ロングタスク検出を含む本番環境モニタリングのためのエントリ収集を自動化します。- Interaction to Next Paint(INP)は、レスポンシブネスに最も密接に関連するCore Web Vitalです。JavaScriptの実行は、スタイル、レイアウト、ペイント処理とともに主要な要因です。
ラボテストとリアルユーザーデータ
ツールに手を伸ばす前に、JavaScriptパフォーマンス測定の2つのタイプを理解しましょう:
- 合成(ラボ)テストは、制御された環境でコードを実行します。LighthouseやChrome DevToolsなどのツールは、再現可能でデバッグ可能な結果を提供します。開発やCIパイプラインに最適です。
- **フィールドデータ(RUM)**は、実際のユーザーが体験する内容をキャプチャします。Chrome User Experience Report(CrUX)やRUMプラットフォームなどのツールは、さまざまなデバイスやネットワークにわたる実際のパフォーマンスを示します。
問題を診断するにはラボツールを使用し、それが重要であることを確認するにはフィールドデータを使用します。
Chrome DevToolsでのJavaScriptプロファイリング
Chrome DevToolsは、JavaScriptパフォーマンスメトリクスの最も実践的な出発点です。Performanceパネルを開き、記録を開始し、ページを操作してから停止します。
注目すべき点:
- ロングタスク — メインスレッドを50ms以上ブロックするタスクは赤色で表示されます。これらはしばしばユーザーインタラクションを遅延させます。最新のツールでは、レスポンシブネスに影響を与える遅いフレームに関するより詳細な洞察を提供するLong Animation Framesも表示される場合があります。
- Call Tree / Bottom-Upビュー — 最も実行時間を消費する関数を特定します。
- フレームチャート — 時間経過に伴うコールスタックを視覚化し、高コストな同期処理を発見します。
Firefox DevToolsも同様のプロファイラーを提供しています。両ツールとも無料で、セットアップ不要、あらゆるサイトで動作します。
Performance APIによるJavaScript実行時間の測定
正確でプログラマティックなJavaScriptパフォーマンス測定には、ブラウザ組み込みのPerformance APIを使用します。
performance.now()の使用
performance.now()は、ページのtime originを基準とした高解像度のタイムスタンプをミリ秒単位で返します。これにより、コードのタイミング測定においてDate.now()よりも信頼性が高くなります。
const start = performance.now()
runExpensiveOperation()
const duration = performance.now() - start
console.log(`Took ${duration}ms`)
performance.mark()とperformance.measure()の使用
複数のポイントにわたる構造化されたタイミング測定には、マークとメジャーを使用します。これはDevToolsやPerformanceObserverと直接統合されます。
performance.mark('fetch-start')
const data = await fetchData()
performance.mark('fetch-end')
const measure = performance.measure('fetch-duration', 'fetch-start', 'fetch-end')
console.log(measure.duration) // ミリ秒
メジャーはChrome DevToolsのPerformanceパネルのTimingsトラックに表示され、メインスレッド上の他のアクティビティと簡単に関連付けることができます。
Discover how at OpenReplay.com.
PerformanceObserverによる測定の自動化
PerformanceObserverを使用すると、パフォーマンスエントリが発生したときに反応できます。これは本番環境のモニタリングに便利です。
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(entry.name, entry.duration)
}
})
observer.observe({ type: 'measure', buffered: true })
また、longtaskエントリを監視して、実際のユーザーセッションでのメインスレッドのブロッキングを検出することもできます。
Core Web VitalsとINP:重要なメトリクス
GoogleのCore Web Vitalsは、ユーザーエクスペリエンスのための標準的なJavaScriptパフォーマンスメトリクスです。JavaScriptに最も関連するのは**Interaction to Next Paint(INP)**で、ページのライフタイム全体にわたるすべてのインタラクション(クリック、タップ、キーボード入力)のレイテンシを測定します。
INPが200msを超えると警告サインです。500msを超えると不良です。イベントハンドラー実行中の重いJavaScript実行が最も一般的な原因です。
フィールドでINPを測定するには、web-vitalsライブラリを使用します:
import { onINP } from 'web-vitals'
onINP(({ value }) => console.log('INP:', value))
SPAの場合、ソフトナビゲーション(完全なページロードなしのルート変更)は、標準的なナビゲーションメトリクスでは部分的にしかキャプチャされないことに注意してください。ソフトナビゲーション測定のブラウザサポートはまだ進化中であるため、performance.mark()を使用してルート遷移を手動で計測することでギャップを埋めることができます。
適切なツールの選択
| 目的 | ツール |
|---|---|
| クイックデバッグ | console.time() / console.timeEnd() |
| 正確なタイミング | performance.now() |
| 構造化された視覚的タイミング | performance.mark() + performance.measure() |
| 自動モニタリング | PerformanceObserver |
| ページ全体のプロファイリング | Chrome DevTools Performanceパネル |
| 監査スコア + フィールドデータ | Lighthouse + CrUX |
まとめ
効果的なJavaScriptパフォーマンス測定は、適切な質問に対する適切なツールから始まります。プロファイリングと探索にはDevToolsを使用し、特定のコードパスを計測するにはPerformance APIを使用し、ユーザーが実際に体験する内容を理解するにはCore Web Vitals、特にINPを使用します。まず測定し、それから最適化しましょう。
よくある質問
performance.now()は、ページのtime originを基準とした高解像度のタイムスタンプを返し、ミリ秒未満の精度を提供します。Date.now()はシステムクロックに依存し、時刻調整の影響を受ける可能性があり、ミリ秒の解像度しか提供しません。コード実行のベンチマークには、performance.now()がより正確で信頼性の高い選択肢です。
longtaskタイプのエントリを監視するように設定されたPerformanceObserverを使用します。メインスレッドで50msを超えるタスクは、ロングタスクとしてフラグが立てられます。本番環境でこれらのエントリを収集することで、どのユーザーインタラクションがブロッキング処理をトリガーするかを特定し、最も重要な箇所の最適化に優先順位を付けることができます。
Interaction to Next Paintは、ページ訪問中のすべてのインタラクションのレイテンシを測定し、最悪のものを報告します。First Input Delayは最初のインタラクションの遅延のみをキャプチャしました。INPはランタイムのレスポンシブネスのより完全な全体像を提供するため、Googleは2024年3月にFIDをINPに置き換えてCore Web Vitalとしました。
はい。await式の前後にperformance.mark()呼び出しを配置し、両方のマーク名を使用してperformance.measure()を呼び出すことで、経過時間を計算できます。結果のメジャーエントリには非同期操作の完全な期間が含まれ、視覚的な関連付けのためにDevToolsのTimingsトラックに表示されます。
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Check our GitHub repo and join the thousands of developers in our community.