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 > [設定] > [無視リスト] で確認できます。

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 フォルダが非表示になりました。

Chrome DevTools のファイルツリーのスクリーンショット。作成コードは表示されていますが、node_modules は表示されていません。

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

無視リストに登録されたコードは、ファイルツリーから非表示になるだけでなく、[クイック開く] メニュー(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 デバッグの改善では、これを実装する方法について説明しています。