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

はじめに

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

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

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

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

概して、記述時の独自のコードをデバッグしたくなるかもしれません。

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

早速見てみよう。

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

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

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

これはあまり便利ではなく、把握が難しいことが多いです。作成者は、デプロイされたコードではなく、自分で記述したコードを表示してデバッグしたいと考えています。

これを埋めるために、ツリーに [Authored Code] を表示させることができます。これにより、IDE で目にするソースファイルによく似たツリーが表示されます。また、これらのファイルは、デプロイされたコードから分離されます。

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

Chrome DevTools でこのオプションを有効にするには、[設定] >[Experiments] と [ソースをオーサリング ツリーとデプロイ ツリーにグループ化] チェックボックスをオンにします。

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

「自分のコードだけ」

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

これを埋めるために、DevTools の追加設定 [Automatically add knownparty scripts to ignore list] はデフォルトで有効になっています。DevTools >[設定] >無視リスト:

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

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

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

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

スタック トレースで無視リストに記載されているコード

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

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

スタック トレースのすべての呼び出しフレームを表示したい場合は、いつでも [Show more アセット] リンクをクリックします。

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

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

ファイルツリーの無視リストに記載されているコード

無視リストに含まれるファイルとフォルダを [Sources] パネルの [Authored Code] ファイルツリーで非表示にするには、[Settings] >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」という新機能のおかげで、非同期コードの両部分をリンクさせることで、全体像を把握できます。

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