Chrome DevTools での最新のウェブ デバッグ

はじめに

現在、作成者は多くの抽象化を使用してウェブ アプリケーションを構築できます。多くの作成者は、ウェブ プラットフォームが提供する低レベル API と直接やり取りするのではなく、フレームワーク、ビルドツール、コンパイラを活用して、より高いレベルの視点からアプリケーションを記述します。

たとえば、Angular フレームワーク上に構築されたコンポーネントは、HTML テンプレートを使用して TypeScript で作成されます。内部では Angular CLI と webpack によってすべてが JavaScript にコンパイルされ、いわゆる「バンドル」にまとめられます。バンドルはブラウザに送られます。

現在、DevTools でウェブ アプリケーションをデバッグまたはプロファイリングする場合、実際に記述したコードではなく、このコンパイル済みバージョンのコードが表示され、デバッグできます。ただし、著者としては望ましいことではありません。

  • 圧縮された JavaScript コードをデバッグするのではなく、元の JavaScript コードをデバッグする場合。
  • TypeScript を使用する場合は、JavaScript ではなく、元の TypeScript コードをデバッグする必要があります。
  • Angular、Lit、JSX などのテンプレートを使用する場合、生成された DOM をデバッグする必要がない場合があります。コンポーネント自体をデバッグする必要がある場合。

全体的に、独自のコードを作成したときと同じようにデバッグすることをおすすめします。

ソースマップはすでにこのギャップをある程度埋めていますが、Chrome DevTools とエコシステムが実現できるこの領域はまだまだあります。

早速見てみよう。

作成されたコードとデプロイされたコード

現在、[ソースパネル] でファイルツリーを操作すると、コンパイルされた(多くの場合、圧縮された)バンドルの内容が表示されます。これらは、ブラウザがダウンロードして実行する実際のファイルです。DevTools では、これを「デプロイされたコード」と呼びます。

デプロイされたコードを示す Chrome DevTools のファイルツリーのスクリーンショット。

これはあまり便利ではなく、把握しづらいことがよくあります。作成者は、デプロイされたコードではなく、作成したコードを表示してデバッグする必要があります。

これを補うため、代わりに作成元のコードをツリーに表示できるようになりました。これにより、ツリーは IDE で表示されるソースファイルに近づき、これらのファイルは [デプロイされたコード] から分離されました。

作成されたコードを示す Chrome DevTools のファイルツリーのスクリーンショット。

Chrome DevTools でこのオプションを有効にするには、[設定] > [試験運用版] に移動し、[ソースを作成済みツリーとデプロイ済みツリーにグループ化] をオンにします。

DevTools の [Settings] のスクリーンショット。

[自分のコードのみ]

依存関係を使用する場合やフレームワーク上にビルドする場合、サードパーティ ファイルが邪魔になることがあります。ほとんどの場合、node_modules フォルダに隠れているサードパーティ ライブラリのコードではなく、自分のコードのみを表示する必要があります。

これを補うため、DevTools にはデフォルトで追加の設定(既知のサードパーティ スクリプトを無視リストに自動追加)が有効になっています。これは、[DevTools] > [Settings] > [Ignore List] で確認できます。

DevTools の [Settings] のスクリーンショット。

この設定を有効にすると、フレームワークまたはビルドツールが「無視する」とマークしたファイルまたはフォルダが DevTools で非表示になります。

Angular v14.1.0 以降、node_modules フォルダと webpack フォルダの内容には、そのようにマークが付けられています。そのため、これらのフォルダ、その中のファイル、その他のサードパーティ アーティファクトは、DevTools のさまざまな場所に表示されません。

著者側でこの新しい動作を有効にするための操作は必要ありません。この変更を実装するのはフレームワーク次第です。

スタック トレース内の無視リストに登録されたコード

無視リストに登録されたファイルが、スタック トレースには表示されなくなりました。作成者は、より関連性の高いスタック トレースを確認できるようになりました。

DevTools のスタック トレースのスナップショット。

スタック トレース内のすべての呼び出しフレームを表示するには、[Show more frames] リンクをクリックします。

コードのデバッグとステップスルー中に表示されるコールスタックにも同じことが言えます。フレームワークまたはバンドラがサードパーティ スクリプトについて DevTools に通知すると、DevTools は関連のない呼び出しフレームをすべて自動的に非表示にし、ステップ デバッグ中に無視リストに登録されたコードを飛ばします。

デバッグ中の DevTools ソース デバッガのスクリーンショット。

ファイルツリー内の無視リストに登録されたコード

無視リストに登録されたファイルとフォルダを [ソース] パネルの [作成者コード] ファイルツリーから非表示にするには、DevTools の [設定] > [試験運用版] で [ソースツリービューで無視リストに登録されたコードを非表示にする] をオンにします。

DevTools の [Settings] のスクリーンショット。

サンプルの Angular プロジェクトでは、node_modules フォルダと webpack フォルダが非表示になりました。

作成済みのコードは表示されるが node_modules が表示されない、Chrome DevTools のファイルツリーのスクリーンショット。

[クイック開く] メニューの無視リストに登録されたコード

無視リストに登録されたコードは、ファイルツリーから非表示になるだけでなく、[クイック開く] メニュー(Ctrl+P(Linux / Windows)または Command+P(Mac))からも非表示になります。

[クイック開く] メニューのある DevTools のスクリーンショット。

スタック トレースのさらなる改善

関連性の高いスタック トレースはすでにカバーされていますが、Chrome DevTools ではスタック トレースがさらに改善されています。

リンクされたスタック トレース

一部のオペレーションが非同期的に実行されるようにスケジュールされている場合、DevTools のスタック トレースでは現時点で一部の情報しかわかりません。

たとえば、架空の framework.js ファイル内の非常にシンプルなスケジューラを次に示します。

function makeScheduler() {
  const tasks = [];

  return {
    schedule(f) {
      tasks.push({ f });
    },

    work() {
      while (tasks.length) {
        const { f } = tasks.shift();
        f();
      }
    },
  };
}

const scheduler = makeScheduler();

function loop() {
  scheduler.work();
  requestAnimationFrame(loop);
};

loop();

デベロッパーが example.js ファイルの独自のコードで使用する方法は次のとおりです。

function someTask() {
  console.trace("done!");
}

function businessLogic() {
  scheduler.schedule(someTask);
}

businessLogic();

someTask メソッド内にブレークポイントを追加した場合や、コンソールに出力されたトレースを調べた場合、このオペレーションの「根本原因」である businessLogic() 呼び出しに関する記述は見つかりませんでした。

タスク実行につながったフレームワーク スケジューリング ロジックのみが表示され、スタック トレースにはパンくずリストが表示されないため、このタスクに至るイベント間の因果関係を把握するのに役立ちます。

非同期で実行されたコードのスタック トレース。スケジュールされた日時に関する情報はありません。

「非同期スタック タグ付け」という新機能により、非同期コードの両方の部分をリンクすることで、全体像を把握できるようになりました。

Async Stack Tagging API に、console.createTask() という新しい console メソッドが導入されました。API シグネチャは次のとおりです。

interface Console {
  createTask(name: string): Task;
}

interface Task {
  run<T>(f: () => T): T;
}

console.createTask() 呼び出しは Task インスタンスを返します。このインスタンスは、後でタスクのコンテンツ f の実行に使用できます。

// Task Creation
const task = console.createTask(name);

// Task Execution
task.run(f);

タスクは、作成されたコンテキストと実行中の非同期関数のコンテキストをリンクします。

上記の makeScheduler 関数に適用すると、コードは次のようになります。

function makeScheduler() {
  const tasks = [];

  return {
    schedule(f) {
      const task = console.createTask(f.name);
      tasks.push({ task, f });
    },

    work() {
      while (tasks.length) {
        const { task, f } = tasks.shift();
        task.run(f); // instead of f();
      }
    },
  };
}

これにより、Chrome DevTools でより正確なスタック トレースを表示できるようになりました。

非同期で実行されたコードのスタック トレース(スケジュールされた時刻に関する情報を含む)。

businessLogic() がスタック トレース内に含まれていることに注目してください。それだけでなく、タスクには、以前の一般的な requestAnimationFrame ではなく、わかりやすい名前 someTask が付けられます。

フレンドリーな呼び出しフレーム

フレームワークは、プロジェクトの構築時にさまざまなテンプレート言語からコードを生成します。たとえば、Angular や JSX テンプレートでは、HTML に似たコードを、最終的にブラウザで実行される単純な JavaScript に変換します。生成されたこのような関数の中には、あまり親しみのない名前が付けられることもあります。たとえば、圧縮された後に 1 文字の名前であったり、そうでなくても不明瞭な名前やなじみのない名前であったりします。

サンプル プロジェクトにおけるこの例は、スタック トレースに表示される AppComponent_Template_app_button_handleClick_1_listener です。

自動生成された関数名を含むスタック トレースのスナップショット。

この問題に対処するため、Chrome DevTools ではソースマップを使用してこれらの関数の名前変更がサポートされるようになりました。ソースマップに関数スコープの開始点の名前エントリがある場合、コールフレームはその名前をスタックトレース内に表示します。

著者側でこの新しい動作を有効にするための操作は必要ありません。この変更を実装するのはフレームワーク次第です。

今後に向けて

この投稿で説明した機能の追加により、Chrome DevTools でより快適にデバッグできるようになりました。チームは、他にも検討したい領域があります。特に、DevTools でのプロファイリングの使い勝手を改善する方法について説明します。

Chrome DevTools チームは、フレームワークの作成者にこれらの新機能を導入することを推奨しています。事例紹介: DevTools を使用した Angular デバッグの改善では、この実装方法に関するガイダンスを提供しています。