Modernes Web-Debugging in den Chrome-Entwicklertools

Einführung

Heute können Entwickler viele Abstraktionsschichten verwenden, um ihre Webanwendungen zu erstellen. Anstatt direkt mit den APIs der unteren Ebene zu interagieren, die die Webplattform bietet, nutzen viele Entwickler Frameworks, Build-Tools und Compiler, um ihre Anwendungen aus einer höheren Perspektive zu erstellen.

Komponenten, die auf dem Angular-Framework basieren, werden beispielsweise in TypeScript mit HTML-Vorlagen erstellt. Im Hintergrund kompilieren die Angular-Befehlszeile und webpack alles in JavaScript und in ein sogenanntes Bundle, das dann an den Browser gesendet wird.

Wenn Sie Webanwendungen in DevTools debuggen oder profilieren, sehen Sie derzeit diese kompilierte Version Ihres Codes anstelle des von Ihnen geschriebenen Codes. Als Autor möchten Sie das jedoch nicht:

  • Sie möchten nicht den minimierten JavaScript-Code debuggen, sondern den ursprünglichen JavaScript-Code.
  • Wenn Sie TypeScript verwenden, sollten Sie nicht JavaScript, sondern den ursprünglichen TypeScript-Code debuggen.
  • Wenn Sie templating wie mit Angular, Lit oder JSX verwenden, möchten Sie nicht immer das resultierende DOM debuggen. Möglicherweise möchten Sie die Komponenten selbst beheben.

Im Allgemeinen sollten Sie Ihren eigenen Code so debuggen, wie Sie ihn geschrieben haben.

Quellkarten schließen diese Lücke bereits teilweise, aber die Chrome-Entwicklertools und das gesamte System können in diesem Bereich noch mehr leisten.

Sehen wir uns das genauer an!

Erstellter und bereitgestellter Code

Wenn Sie derzeit im Dateibaum im Bereich Quellen navigieren, sehen Sie den Inhalt des kompilierten und oft minimierten Bundles. Das sind die tatsächlichen Dateien, die der Browser herunterlädt und ausführt. In den Entwicklertools wird dies als Bereitgestellter Code bezeichnet.

Screenshot des Dateibaums in den Chrome-Entwicklertools mit dem bereitgestellten Code

Das ist nicht sehr praktisch und oft schwer zu verstehen. Als Autor möchten Sie den von Ihnen geschriebenen Code sehen und debuggen, nicht den bereitgestellten Code.

Stattdessen können Sie jetzt den vom Nutzer erstellten Code im Stammbaum anzeigen lassen. Dadurch ähnelt der Verzweigungsbaum den Quelldateien, die Sie in Ihrer IDE sehen. Diese Dateien sind jetzt vom bereitgestellten Code getrennt.

Screenshot des Dateibaums in den Chrome-Entwicklertools mit dem Autorisierten Code

Wenn Sie diese Option in den Chrome-Entwicklertools aktivieren möchten, klicken Sie auf Einstellungen > Tests und setzen Sie ein Häkchen bei Quellen in Verzweigungen für erstellte und bereitgestellte Seiten gruppieren.

Screenshot der Einstellungen in den Entwicklertools

„Nur mein Code“

Wenn Sie Abhängigkeiten verwenden oder auf einem Framework aufbauen, können die Drittanbieterdateien in die Quere kommen. In den meisten Fällen möchten Sie nur Ihren Code sehen, nicht den einer Drittanbieterbibliothek, die im Ordner node_modules versteckt ist.

Daher ist in den DevTools standardmäßig eine zusätzliche Einstellung aktiviert: Bekannte Drittanbieterskripts automatisch der Ignorieren-Liste hinzufügen. Sie finden sie unter DevTools > Einstellungen > Ignorierliste.

Screenshot der Einstellungen in den Entwicklertools

Wenn diese Einstellung aktiviert ist, werden in den DevTools alle Dateien oder Ordner ausgeblendet, die von einem Framework oder Build-Tool als zu ignorieren gekennzeichnet wurden.

Seit Angular v14.1.0 sind die Inhalte der Ordner node_modules und webpack entsprechend gekennzeichnet. Daher werden diese Ordner, die darin enthaltenen Dateien und andere ähnliche Drittanbieter-Artefakte an verschiedenen Stellen in DevTools nicht angezeigt.

Als Autor müssen Sie nichts weiter tun, um dieses neue Verhalten zu aktivieren. Die Implementierung dieser Änderung obliegt dem Framework.

Code auf der Ignorierliste in Stack-Traces

Beispielsweise werden diese Dateien nicht mehr in Stack-Traces angezeigt. Als Entwickler sehen Sie jetzt mehr relevante Stack-Traces.

Screenshot eines Stack-Traces in den DevTools

Wenn Sie alle Aufrufframes des Stack-Traces sehen möchten, können Sie jederzeit auf den Link Weitere Frames anzeigen klicken.

Gleiches gilt für die Aufrufstacks, die beim Debuggen und Schritt-für-Schritt-Ausführen des Codes angezeigt werden. Wenn Frameworks oder Bundler DevTools über Drittanbieter-Scripts informieren, werden in DevTools automatisch alle irrelevanten Aufrufframes ausgeblendet und beim Schritt-für-Schritt-Debugging wird Code, der auf der Ignorierliste steht, übersprungen.

Screenshot des DevTools-Debuggers für Quellen während des Debuggens

Code auf der Ignorieren-Liste im Dateibaum

Wenn Sie die Dateien und Ordner in der Ignorierliste im Dateibaum Autorisierter Code im Bereich Quellen ausblenden möchten, aktivieren Sie in den DevTools unter Einstellungen > Experimente die Option Code in der Ignorierliste in der Strukturansicht der Quellen ausblenden.

Screenshot der Einstellungen in den Entwicklertools

Im Beispiel-Angular-Projekt sind die Ordner node_modules und webpack jetzt ausgeblendet.

Screenshot des Dateibaums in den Chrome-Entwicklertools, in dem der erstellte Code, aber nicht „node_modules“ angezeigt wird

Ignorierte Codes im Menü „Schnell öffnen“

Code in der Ignorierliste wird nicht nur im Dateibaum ausgeblendet, sondern auch im Menü „Schnell öffnen“ (Strg + P (Linux/Windows) oder Befehl + P (Mac)).

Screenshot der Entwicklertools mit dem Menü „Schnell öffnen“

Weitere Verbesserungen an Stack-Traces

Wir haben bereits relevante Stack-Traces behandelt. In den Chrome-Entwicklertools gibt es aber noch weitere Verbesserungen.

Verknüpfte Stack-Traces

Wenn einige Vorgänge asynchron ausgeführt werden, geben die Stack-Traces in den DevTools derzeit nur einen Teil der Informationen preis.

Hier ist beispielsweise ein sehr einfacher Zeitplaner in einer hypothetischen framework.js-Datei:

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();

… und wie ein Entwickler sie in seinem eigenen Code in einer example.js-Datei verwenden könnte:

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

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

businessLogic();

Wenn Sie in der Methode someTask einen Haltepunkt einfügen oder den in der Konsole gedruckten Trace prüfen, wird der businessLogic()-Aufruf, der die „Ursache“ dieses Vorgangs war, nicht erwähnt.

Stattdessen sehen Sie nur die Framework-Planungslogik, die zur Ausführung der Aufgabe geführt hat, und keine Breadcrumbs im Stack-Trace, mit denen Sie die kausalen Verknüpfungen zwischen Ereignissen ermitteln können, die zu dieser Aufgabe geführt haben.

Ein Stack-Trace eines asynchron ausgeführten Codes ohne Informationen dazu, wann er geplant wurde.

Dank einer neuen Funktion namens „Async Stack Tagging“ ist es jedoch möglich, die gesamte Geschichte zu erzählen, indem beide Teile des asynchronen Codes miteinander verknüpft werden.

Die Async Stack Tagging API führt eine neue console-Methode namens console.createTask() ein. Die API-Signatur sieht so aus:

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

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

Der console.createTask()-Aufruf gibt eine Task-Instanz zurück, mit der Sie später den Inhalt der Aufgabe f ausführen können.

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

// Task Execution
task.run(f);

Die Aufgabe bildet die Verknüpfung zwischen dem Kontext, in dem sie erstellt wurde, und dem Kontext der asynchronen Funktion, die ausgeführt wird.

Angewandt auf die makeScheduler-Funktion oben sieht der Code so aus:

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();
      }
    },
  };
}

Dadurch können in den Chrome-Entwicklertools jetzt bessere Stack-Traces angezeigt werden.

Ein Stack-Trace eines asynchron ausgeführten Codes mit Informationen dazu, wann er geplant wurde.

Beachten Sie, dass businessLogic() jetzt im Stacktrace enthalten ist. Außerdem hat die Aufgabe einen bekannten Namen, someTask, anstelle des generischen requestAnimationFrame wie zuvor.

Frames für freundliche Anrufe

Frameworks generieren beim Erstellen eines Projekts häufig Code aus allen Arten von Vorlagensprachen, z. B. Angular- oder JSX-Vorlagen, die HTML-ähnlichen Code in einfachen JavaScript-Code umwandeln, der schließlich im Browser ausgeführt wird. Manchmal werden diesen generierten Funktionen nicht sehr nutzerfreundliche Namen gegeben – entweder ein einzelner Buchstabe nach dem Minimieren oder unklare oder unbekannte Namen, auch wenn sie es nicht sind.

Im Beispielprojekt ist das AppComponent_Template_app_button_handleClick_1_listener, das im Stack-Trace zu sehen ist.

Screenshot eines Stack-Traces mit einem automatisch generierten Funktionsnamen

In den Chrome-Entwicklertools können Sie diese Funktionen jetzt über Quellkarten umbenennen. Wenn eine Quellkarte einen Namenseintrag für den Beginn eines Funktionsbereichs enthält, sollte der Aufrufframe diesen Namen im Stack-Trace anzeigen.

Als Autor müssen Sie nichts weiter tun, um dieses neue Verhalten zu aktivieren. Die Implementierung dieser Änderung obliegt dem Framework.

Zukunftspläne

Dank der in diesem Beitrag beschriebenen Ergänzungen können Sie die Chrome-Entwicklertools noch effektiver für das Debuggen verwenden. Es gibt noch weitere Bereiche, die das Team erkunden möchte. Insbesondere geht es darum, wie das Profiling in den DevTools verbessert werden kann.

Das Chrome DevTools-Team empfiehlt Framework-Entwicklern, diese neuen Funktionen zu nutzen. In der Fallstudie: Besseres Angular-Debugging mit DevTools finden Sie eine Anleitung dazu.