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

はじめに

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

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

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

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

概して、自分のコードを自分が記述しているとおりにデバッグしたくなるかもしれません。

ソースマップはすでにこのギャップをある程度解消していますが、この分野で Chrome DevTools とエコシステムでできることは他にもあります。

早速見てみよう。

作成済みコードとデプロイ済みコード

現在、[Sources] パネルでファイルツリーを移動すると、コンパイル済み(多くの場合は圧縮済み)のバンドルの内容を確認できます。これらは、ブラウザがダウンロードして実行する実際のファイルです。DevTools ではこれを「デプロイ済みコード」と呼びます。

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

これはあまり便利ではなく、多くの場合把握しにくいものです。作成者は、デプロイされたコードではなく、作成したコードを確認してデバッグする必要があります。

代わりに、ツリーに [Authored Code] を表示するようにしました。これにより、このツリーは IDE に表示されるソースファイルによく似ており、これらのファイルはデプロイ済みコードから分離されています。

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

このオプションを Chrome DevTools で有効にするには、[Settings] > [Experiments] に移動し、[Group source into Authored and Deployed tree] をオンにします。

DevTools の設定のスクリーンショット。

「自分のコードだけ」

依存関係を使用したり、フレームワーク上にビルドしたりする場合は、サードパーティのファイルが妨げになる可能性があります。ほとんどの場合、node_modules フォルダに格納されたサードパーティ ライブラリではなく、自分のコードだけを確認する必要があります。

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

DevTools の設定のスクリーンショット。

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

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

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

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

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

DevTools のスタック トレースのスクリーンショット。

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

コードのデバッグやステップ実行中に表示されるコールスタックについても同様です。フレームワークまたはバンドラがサードパーティ スクリプトについて DevTools に通知すると、DevTools はステップ デバッグ中に、すべての無関係な呼び出しフレームを自動的に非表示にし、無視リストに記載されているコードにジャンプします。

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

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

無視リストに登録されているファイルとフォルダを [ソース] パネルの [作成済みコード] ファイルツリーで非表示にするには、DevTools の [設定] > [テスト] で、[無視リストに登録されているコードをソースツリー ビューで非表示にする] チェックボックスをオンにします。

DevTools の設定のスクリーンショット。

サンプルの 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 が付いています。

フレンドリーな通話フレーム

フレームワークでは多くの場合、プロジェクトの作成時にあらゆる種類のテンプレート言語からコードが生成されます。たとえば、HTML に見えるコードをプレーン JavaScript に変換し、最終的にブラウザ内で実行する Angular や JSX のテンプレートなどがあります。この種の生成された関数は、あまり親しみのない名前が付けられることがあります。圧縮された後の 1 文字の名前や、そうでなくても不明瞭な名前や見慣れない名前などがこれにあたります。

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

自動生成された関数名が表示されたスタック トレースのスクリーンショット。

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

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

今後に向けて

この投稿でご紹介した追加機能のおかげで、Chrome DevTools のデバッグ エクスペリエンスが向上します。チームが検討したい分野はさらにあります。具体的には、DevTools でのプロファイリング エクスペリエンスを改善する方法に関するものです。

Chrome DevTools チームでは、フレームワーク作成者にこれらの新機能の導入を推奨しています。実装方法については、事例紹介: Better Angular Debugging with DevTools をご覧ください。