Chrome DevTools の [Performance] パネルを使用して Angular アプリをプロファイリングする

Andrés Olivares
Andrés Olivares
Pawel Kozlowski
Pawel Kozlowski

Angular、React、Vue、Svelte などのウェブ フレームワークを使用すると、複雑なウェブ アプリケーションを大規模に作成して保守することが容易になります。

ただし、これらのフレームワークは、ブラウザのアプリケーション モデルの上に抽象化レイヤを導入します。実際、これらの抽象化を使用してデベロッパーが記述したコードは、通常、読みにくく、最小化され、バンドルされたコードにトランスパイルされます。そのため、デベロッパーが DevTools の機能を十分に活用してこれらのアプリをデバッグし、プロファイリングすることは困難です。

たとえば、DevTools の [パフォーマンス] パネルで Angular アプリケーションのプロファイリングを行うと、次のような結果が表示されます。

Angular アプリから取得したページ読み込みのタイムラインを示すパフォーマンス パネルのトレースビュー。展開されたメインスレッドのスイムレーンに焦点が当てられており、JavaScript 呼び出しのフレームチャートが短縮名で表示されている。
[パフォーマンス] パネルのトレース ビュー。

このように情報が表示されると、コードベースに存在するパフォーマンスのボトルネックを特定するのが難しくなります。結局のところ、フレームワークの構成要素のコンテキストが欠落しており、表示される情報の大部分が縮小コードで記述されています。また、記述したコードに直接関連するアクティビティ、フレームワークの内部処理、同じページで実行されている可能性のある他のサードパーティ コードを区別することも困難です。

フレームワークと抽象化の作成者の一般的な動機は、フレームワークのコンセプトに基づいてプロファイリング データを表示する独自の DevTools 拡張機能を実装することです。これらのツールは、特定のフレームワークで構築されたアプリケーションのデバッグとプロファイリングを行う場合に非常に便利です。ただし、フレームワークの独自のプロファイラにあるフレームワーク データと、DevTools の [パフォーマンス] パネルにあるブラウザのランタイム情報を関連付ける必要が生じることはよくあります。これらの 2 つのデータソースが別々のツールで個別に表示されると、特にアプリケーションが複雑になるにつれて、ボトルネックを特定して修正することが難しくなります。Angular DevTools Profiler のプロファイル可視化の例を次に示します。

Angular DevTools の [Profiler] タブ。Angular アプリのランタイムのフレームグラフが表示されています。フレームグラフを構成する項目には、Angular コンポーネント名に似た読みやすいラベルが付いています。
Angular DevTools Profiler。

理想的には、2 つのデータソースが同じコンテキストで同じタイムラインにマッピングされて一緒に表示されるビューが開発者に提供されることが望ましいでしょう。

そのため、Angular チームと協力して、パフォーマンス パネル拡張機能 API を使用して Angular ランタイム データを [パフォーマンス] パネルに直接取り込みました。この投稿では、API で何ができるか、Angular フレームワークで API を使用してこれを実現する方法について説明します。この実装は、独自のツールを計測して Chrome DevTools を使用するデベロッパーを支援することで、デベロッパー エクスペリエンスの向上を目指す他のフレームワークや抽象化の例として役立ちます。

パフォーマンス パネルの拡張性 API とは何ですか?

この API を使用すると、ブラウザの他のデータと同じタイムライン内で、独自のタイミング エントリを [パフォーマンス] パネルのトレースに追加できます。これを行うためのメカニズムが 2 つあります。

  • User Timing API
  • console.timeStampAPI

User Timing API

performance.markperformance.measure を使用して、次のようにエントリを追加できます。


// Mark used to represent the start of some activity you want to measure.
// In this case, the rendering of a component.
const renderStart = performance.now();

// ... later in your code

performance.measure("Component rendering", {
  start: renderStart,
  detail: {
    devtools: {
      dataType: "track-entry",
      track: "Components",
      color: "secondary",
      properties: [
        ["Render reason", "Props changed"],
        ["Priority", "low"]
      ],
    }
  }
});

これにより、タイムラインに測定値を含む [Components] トラックが追加されます。

[パフォーマンス] パネルのトレースビュー。「コンポーネント レンダリング」という測定を含む「コンポーネント」という拡張カスタム トラックに焦点を当てています。
[パフォーマンス] パネルのカスタム トラック。

この API を使用すると、エントリを パフォーマンス タイムライン バッファに追加し、DevTools の [パフォーマンス] パネルの UI にも表示できます。

この API と devtools オブジェクトの詳細については、ドキュメントをご覧ください。

console.timeStampAPI

この API は、User Timing API に代わる軽量な API です。前の例と同じように、次のように記述できます。


// Mark used to represent the start of some activity you want to measure.
// In this case, the rendering of a component.
const renderStart = performance.now();

// ... later in your code

console.timeStamp(
"Component rendering",
/* start time */ renderStart,
/* end time (current time) */ undefined,
/* track name */ "Components",
 /* track group name */ undefined,
 /* color */ "secondary"
);

この API は、アプリケーションを計測するための高性能な方法を提供します。User Timing API の代替手段とは異なり、バッファリングされたデータは作成されません。この API は DevTools の [パフォーマンス] パネルにのみデータを追加します。つまり、DevTools がトレースを記録していない場合、API への呼び出しは no-op(何も行わない)になり、大幅に高速化され、パフォーマンスに敏感なホットパスに適しています。すべてのカスタマイズ パラメータを含むオブジェクトではなく位置引数を使用する選択は、API をできるだけ軽量に保つという目的も果たします。

console.timeStamp を使用してパフォーマンス パネルを拡張する方法と、ドキュメントで渡すことができるパラメータについてご確認ください。

Angular が DevTools 拡張機能 API を統合した方法

Angular チームが拡張機能 API を使用して Chrome DevTools と統合した方法を見てみましょう。

console.timestamp でオーバーヘッドを回避する

Angular の計測と [パフォーマンス] パネルの拡張性 API は、バージョン 20 以降で利用できます。DevTools のパフォーマンス データに求められる粒度を実現するには、高速な API が必要です。そのため、計測機能を追加したプルリクエスト(60217)では、console.timeStamp API を使用することにしました。これにより、プロファイリング API の潜在的なオーバーヘッドがアプリケーションのランタイム パフォーマンスに影響を与えるのを防ぎます。

計測データ

Angular コードが実行されている内容と、そもそもなぜ実行されているのかを把握するために、起動パイプラインとレンダリング パイプラインの複数の部分が計測されます。たとえば、次のような部分です。

  • アプリケーションとコンポーネントのブートストラップ。
  • コンポーネントの作成と更新。
  • イベント リスナーとライフサイクル フックの実行。
  • その他多数(動的コンポーネントの作成、遅延ブロックのレンダリングなど)。

色分け

色分けは、特定の測定エントリがどのカテゴリに分類されるかをデベロッパーに知らせるために使用されます。たとえば、デベロッパーが作成した TypeScript コードの実行を示すエントリに使用される色は、Angular コンパイラによって生成されたコードに使用される色とは異なります。

次のスクリーンショットでは、この結果として、エントリ ポイント(変更検出やコンポーネント処理など)が青色、生成されたコードが紫色、TypeScript コード(イベント リスナーやフックなど)が緑色でレンダリングされている様子を確認できます。

[パフォーマンス] パネルのトレースビュー。このトラックは、Angular アプリのランタイム アクティビティをデベロッパーにわかりやすい方法で表す、さまざまな色で測定値が示されたフレームグラフを含む「Angular」という名前の拡張カスタム トラックに焦点を当てています。
[パフォーマンス] パネルの色分け。

API に渡される color 引数は、CSS の色値ではなく、DevTools UI に一致する色にマッピングされるセマンティック トークンであることに注意してください。指定できる値は primarysecondarytertiary, で、それぞれ -dark-light のバリエーションと error の色があります。

トラック

この時点では、すべての Angular ランタイム データが同じトラック(「🅰️ Angular」というラベルが付いています)に追加されます。ただし、トレースに複数のトラックを追加したり、トラックをグループ化したりすることは可能です。たとえば、console.timeStamp API への次の呼び出しがあるとします。

console.timeStamp("Component 1", componentStart1, componentEnd1, "Components", "Client", "primary");
console.timeStamp("Component 2", componentStart2, componentEnd2, "Components", "Client", "primary");
console.timeStamp("Hook 1", hookStart, hookEnd, "Hooks", "Client", "primary");
console.timeStamp("Fetch data base", fetchStart, fetchEnd, "Server", "primary");

データは次のようにトラックに整理されます。

[パフォーマンス] パネルのトレースビュー。複数の拡張カスタム トラックにそれぞれ異なる測定値が含まれていることに注目してください。
[パフォーマンス] パネルに複数のカスタム トラックが表示されている。

別々のトラックを使用すると、たとえば非同期アクティビティがある場合、複数のジョブが並行して実行されている場合、または UI の異なる領域で分離する価値があるほどアクティビティ グループが明確に区別されている場合に便利です。

Angular デベロッパーにとってのメリット

この直接統合の目的は、より直感的で包括的なパフォーマンス分析エクスペリエンスを提供することです。Angular の内部パフォーマンス データを [**パフォーマンス**] パネルに直接表示することで、デベロッパーは次の情報を取得できます。

  • 可視性の向上: コンポーネントのレンダリングや変更検出サイクルなど、Angular 固有のパフォーマンス イベントをブラウザのタイムライン全体で確認できます。
  • 理解の向上: Angular の内部プロセスに関するコンテキスト豊富な情報により、パフォーマンスのボトルネックをより効果的に特定できます。

統合を有効にする

拡張機能 API の使用は、Angular バージョン 20 以降のデベロッパー ビルドで正式に利用できるようになりました。有効にするには、アプリまたは DevTools コンソールでグローバル ユーティリティ `ng.enableProfiling()` を実行する必要があります。統合の詳細については、[Angular Docs](https://angular.dev/best-practices/profiling-with-chrome-devtools) をご覧ください。

その他の考慮事項

考慮すべき重要な事項を以下に示します。

ソースマップと軽量化されたコード:

ソースマップは、バンドルされたコードや軽量化されたコードと、その作成元のコードとの間のギャップを埋めることを目的とした、広く採用されているツールです。

ソースマップは、バンドルされたアプリの軽量化されたコードの問題を解決するためのものではないのですか?

ソースマップは確かに便利ですが、複雑な圧縮されたウェブアプリをプロファイリングする際の課題を完全に解消するものではありません。ソースマップを使用すると、DevTools で軽量化されたコードを元のソースコードにマッピングできるため、デバッグが容易になります。ただし、パフォーマンス分析にソースマップのみを使用すると、いくつかの制限が生じる可能性があります。たとえば、フレームワークの内部と作成されたコードを視覚的に分離する方法をソースマップだけで選択するのは複雑です。一方、拡張機能 API は、この区別を実現し、デベロッパーが最も便利だと考える方法で提示する柔軟性を提供します。

Chrome DevTools 拡張機能:

DevTools API を使用する Chrome 拡張機能は、devtools を拡張するために広く使用されているツールです。

この API が利用可能になったため、専用のプロファイラ(Chrome DevTools 拡張機能など)は不要になったり、推奨されなくなったりしますか?

いいえ。この API は、Chrome DevTools 拡張機能などの専用プロファイラの開発を置き換えたり、妨げたりすることを目的としたものではありません。これらのツールは、特定のニーズに合わせて調整された特別な機能、可視化、ワークフローを提供できます。パフォーマンス パネルの拡張機能 API は、カスタムデータを パフォーマンス パネルのブラウザの可視化とシームレスに統合することを目的としています。

今後の展望

拡張性 API の見通し。

より多くのフレームワークと抽象化を扱う

他のフレームワークや抽象化でもこの API が採用され、デベロッパーのプロファイリング エクスペリエンスが向上することを期待しています。たとえば、React はフレームワーク用の API の試験運用版を実装しています。この計測により、クライアント コンポーネントとサーバー コンポーネントのレンダリングと、React スケジューリング API のデータが表面化されます。詳細と有効にする方法については、React のページをご覧ください。

本番環境ビルド

この API の目的の一つは、フレームワークや抽象化プロバイダと連携して、本番環境ビルドで計測を導入し、有効にすることです。これにより、これらの抽象化を使用して開発されたアプリのパフォーマンスに大きな影響を与える可能性があります。ユーザーが体験するアプリケーションをデベロッパーがプロファイリングできるようになるためです。console.timeStamp API は、その速度とオーバーヘッドの低さから、この目標を達成できると考えています。ただし、現時点では、フレームワークは API のテストを続けており、どのようなインストルメンテーションがよりスケーラブルでデベロッパーにとって有用であるかを調査しています。