Repository navigation
Debug de
🌐 Diese Seite auf: English · Português · Español · Français · Deutsch
eQuantic.UI stellt professionelle Debugging-Werkzeuge bereit, ähnlich denen von Next.js, mit Funktionen nur für die Entwicklung, die dir helfen, Probleme schnell zu finden und zu beheben.
Das Framework erkennt die Umgebung automatisch über IWebHostEnvironment.IsDevelopment() und macht sie dem Browser bekannt über:
window.__EQ_DEV__; // true in development, false in productionAlle Entwicklungswerkzeuge werden abhängig von diesem Flag geladen.
Der Logger bietet eine einheitliche, mit Präfix versehene Protokollierung, die nur im Entwicklungsmodus ausgegeben wird.
import { logger } from "@equantic/ui-runtime";
// Nur Entwicklung (in der Produktion stumm)
logger.debug("Component state:", state);
logger.info("API call completed");
// Protokolliert immer (auch in der Produktion)
logger.warn("Deprecated API used");
logger.error("Failed to load data:", error);| Methode | Ausgabe | Produktion | Präfix |
|---|---|---|---|
debug() |
Console debug | ❌ Stumm | [eQuantic.UI] |
info() |
Console info | ❌ Stumm | [eQuantic.UI] |
warn() |
Console warn | ✅ Immer | [eQuantic.UI] |
error() |
Console error | ✅ Immer | [eQuantic.UI] |
In den DevTools des Browsers kannst du nach dem Präfix filtern:
[eQuantic.UI]
Der Logger ist in src/eQuantic.UI.Runtime/src/utils/logger.ts implementiert:
const isDev = typeof window !== "undefined" && window.__EQ_DEV__;
export const logger = {
debug(...args: any[]) {
if (isDev) console.debug("[eQuantic.UI]", ...args);
},
info(...args: any[]) {
if (isDev) console.info("[eQuantic.UI]", ...args);
},
warn(...args: any[]) {
console.warn("[eQuantic.UI]", ...args);
},
error(...args: any[]) {
console.error("[eQuantic.UI]", ...args);
},
};Die Fehlerüberlagerung bietet eine Vollbild-Fehleroberfläche im Stil von Next.js, die automatisch erscheint, wenn zur Laufzeit Fehler auftreten.
- Automatische Erfassung: fängt unbehandelte Fehler und Promise-Ablehnungen
-
C#-Aufrufstapel ✨: die Überlagerung versteht Source Maps: sie holt die
.js.mapjedes Bundles, dekodiert sie (src/dev/source-map.ts+src/dev/stack-remapper.ts) und schreibt den Aufrufstapel als die ursprünglichen C#-Rahmen um, dazu einen Ausschnitt der fehlerhaften C#-Quellzeile. Fällt auf die JS-Ansicht zurück, wenn keine Karte verfügbar ist. (Das ist es, was das Versprechen „0 JS-Kenntnisse“ beim Debuggen echt macht: ein C#-Entwickler sieht C#, nicht transpiliertes JavaScript.) -
Tastaturunterstützung:
Escdrücken zum Schließen -
Nur Entwicklung: erscheint nie in der Produktion (über ein dynamisches
import()in der Entwicklung geladen) - Saubere UX: roter Kopf, nichtproportionale Schrift, scrollbarer Inhalt
Die Fehlerüberlagerung zeigt sich automatisch bei:
- Unbehandelten Fehlern: jede nicht gefangene Ausnahme in JavaScript
- Promise-Ablehnungen: unbehandelte asynchrone Fehler
// Dies löst die Fehlerüberlagerung im Entwicklungsmodus aus
throw new Error("Something went wrong");
Promise.reject("Async error");
await fetch("/api/data"); // Wenn der Fetch fehlschlägt und nicht gefangen wird┌─────────────────────────────────────────────┐
│ ⚠️ Build Error Close (Esc)│
├─────────────────────────────────────────────┤
│ │
│ Error message here │
│ │
│ ┌─────────────────────────────────────────┐ │
│ │ Stack trace: │ │
│ │ at MyComponent.render (page.js:42) │ │
│ │ at Reconciler.patch (reconciler.js:12)│ │
│ │ ... │ │
│ └─────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────┤
│ This error overlay only appears in │
│ development. Fix the error to continue. │
└─────────────────────────────────────────────┘
Du kannst Fehler von Hand in der Überlagerung zeigen:
import { errorOverlay } from "@equantic/ui-runtime/dev";
if (window.__EQ_DEV__) {
errorOverlay.show({
message: "Custom error message",
stack: error.stack,
componentStack: "Component hierarchy...",
});
}// Programmatisch leeren
errorOverlay.clear();
// Aktionen des Nutzers
// - Die Esc-Taste drücken
// - Den Knopf "Close" anklickenDie Fehlerüberlagerung ist in src/eQuantic.UI.Runtime/src/dev/error-overlay.ts implementiert:
class ErrorOverlay {
private overlay: HTMLDivElement | null = null;
private errors: ErrorInfo[] = [];
show(error: ErrorInfo) {
if (!window.__EQ_DEV__) return; // Nur Entwicklung
this.errors.push(error);
this.render();
}
clear() {
this.errors = [];
if (this.overlay) {
this.overlay.remove();
this.overlay = null;
}
}
private render() {
// Erzeugt eine Vollbild-Überlagerung mit den Details des Fehlers
}
}
export const errorOverlay = new ErrorOverlay();
// Automatische Fehlererfassung
if (window.__EQ_DEV__) {
window.addEventListener("error", (event) => {
errorOverlay.show({
message: event.message,
stack: event.error?.stack,
});
});
window.addEventListener("unhandledrejection", (event) => {
errorOverlay.show({
message: `Unhandled Promise Rejection: ${event.reason}`,
stack: event.reason?.stack,
});
});
}Ein Debug-Build schreibt neben jedes Modul eine Source Map, von ihm verlinkt und mit dem C#, aus dem das Modul kam, sodass der Debugger des Browsers das C# selbst zeigt.
Chrome DevTools:
- Öffne die DevTools (F12)
- Gehe zum Tab Sources
- Finde die C#-Datei im Baum, benannt nach ihrem Pfad innerhalb des Projekts (
Pages/Home.cs), nie nach dem der Build-Maschine - Setze Haltepunkte direkt in der C#-Quelle
- Untersuche Zustand, Props und lokale Variablen
Was die Karte eines Moduls trägt, ist die Eigenschaft EQuanticSourceMaps, und ihre Vorgabe folgt
der Konfiguration:
EQuanticSourceMaps |
Vorgabe für | Was der Build schreibt |
|---|---|---|
full |
Debug | eine Karte neben jedem Modul, von ihm verlinkt, mit dem C# darin, für den Debugger des Browsers |
external |
(ausdrücklich zu wählen) | eine Karte ohne das C# und ohne den Link, für einen Fehlerberichter, der Karten selbst hochlädt; buns debugId bleibt erhalten |
none |
jede andere Konfiguration | keine Karte, und kein Link |
Eine Karte mit dem C# darin ist die Quelle der Komponente, der Körper eines [ServerAction]
eingeschlossen, und der Ausgabeordner ist ein Web-Root: darum schreibt nur Debug eine. Was die
Eigenschaft auch sagt, eine Veröffentlichung nimmt keine Karte aus der Ausgabe des Compilers. Um
Karten für einen bereitgestellten Build zu behalten, schreibe sie ohne das C#:
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<EQuanticSourceMaps>external</EQuanticSourceMaps>
</PropertyGroup>eqc setzt jede Karte selbst zusammen, in C#, ohne dass etwas installiert ist: bun bildet das
JavaScript auf das TypeScript ab, das eqc geschrieben hat, eqcs eigene V3-Karte führt von diesem
TypeScript zum C#, und die beiden werden eine Karte von dem JavaScript, das der Browser ausführt, zum
C#. Ihre Quellen werden relativ zur Karte und innerhalb des Projekts benannt, sodass ein Debugger den
eigenen Pfad des Projekts zeigt; eine Datei von außerhalb des Projekts (die Quellen eines Pakets im
NuGet-Cache) wird allein nach ihrem Dateinamen benannt. Ein Modul, das aus mehr als einer Datei
geschrieben ist, benennt jede davon, mit ihrem eigenen C#: eine Klasse, die den Default nimmt, den
eine Schnittstelle liefert, führt die Zeilen des Defaults zur Datei der Schnittstelle, wo der
Debugger sie zeigt (Seit 0.2.0-preview.60). Eine Karte ist JSON, was das C# auch enthält, ein
Seitenvorschub oder jedes andere Steuerzeichen in einem Kommentar eingeschlossen (Seit
0.2.0-preview.60). Eine full-Karte, gekürzt:
{
"version": 3,
"sources": ["../../Pages/Home.cs"],
"sourcesContent": ["…the C# of Pages/Home.cs…"],
"mappings": "AAAA;AACA;..."
}Jede Anweisung hat ein eigenes Segment (Seit 0.2.0-preview.58): ein Rahmen oder ein Haltepunkt
landet auf der C#-Anweisung, die geworfen hat oder auf der er gesetzt wurde, nicht auf der ersten
Zeile ihrer Methode. Eine Zeile, die der Compiler von sich aus schreibt, gehört zu der Anweisung, die
sie erzeugt hat: das else if eines Muster-Switch bildet auf sein case ab, das Dispose eines
using auf das using, die Bedingung eines do auf sich selbst, und die Anweisungen einer lokalen
Funktion und der Ausdruck eines Mitglieds mit Ausdruckskörper auf ihre eigenen Zeilen. Der Block
einer Lambda bildet ebenfalls Anweisung für Anweisung ab (Seit 0.2.0-preview.60), und was dem Block
auf seiner Schlusszeile folgt, bildet wieder auf die Anweisung ab. Das gilt für eine Lambda, die an
eine Instanz- oder statische Methode der App selbst übergeben wird, an eine eigene Methode einer
Liste (ForEach, Find, Exists…) oder an Where, Select, Any, All, Aggregate und die
anderen LINQ-Operatoren, die als eine Gestalt geschrieben sind, für eine in einem Sammlungsausdruck
(children: [ … ]), und für eine, die von einer lokalen Variable, einer lokalen Funktion oder einem
delegate gehalten wird; und seit derselben Version für eine innerhalb eines Mitglieds mit
Ausdruckskörper, einer Objekterzeugung, eines Objekt- oder Sammlungsinitialisierers oder eines
anonymen Objekts. Jede Anweisung eines Körpers mit einem out- oder ref-Parameter bildet ebenfalls
auf ihre eigene Zeile ab. Dieser Körper läuft innerhalb einer Pfeilfunktion, die der Compiler
hinzufügt, also hat ein im Browser gelesener Stapel einen Rahmen mehr als .NETs zwischen dem Werfen
und dem Aufrufer. Anderswo bildet der Block einer Lambda vorerst auf die Anweisung ab, die ihn hält:
an eine Erweiterungsmethode, an einen Delegaten oder über ?. übergeben, an First, Count, Sum,
OrderBy, GroupBy und die anderen Operatoren mit eigener Übersetzung gegeben.
Das erlaubt dir:
- Haltepunkte in C#-Code zu setzen
- Durch C#-Logik zu schreiten
- C#-Variablennamen zu untersuchen
- Ursprüngliche Zeilennummern in Aufrufstapeln zu sehen
Um Zustand und Props einer Komponente zu untersuchen:
// In der Konsole des Browsers
window.__EQ_DEBUG = true; // Aktiviert den Debug-Modus
// Komponenten machen ihren Zustand bekannt
const component = document.querySelector(
'[data-component-id="abc"]',
).__component;
console.log(component.state);
console.log(component.props);Zum Debuggen von Rendering-Problemen zwischen SSR und CSR:
test("SSR matches CSR", async ({ page }) => {
// Das HTML des SSR holen
const ssrResponse = await page.goto("http://localhost:5000");
const ssrHtml = await ssrResponse.text();
// Auf die Hydration des CSR warten
await page.waitForLoadState("networkidle");
const csrHtml = await page.content();
// Vergleichen
expect(normalizeHtml(ssrHtml)).toBe(normalizeHtml(csrHtml));
});Debugge C#-Code normal mit Visual Studio oder VS Code:
- Setze Haltepunkte in
.cs-Dateien - Starte mit angehängtem Debugger:
dotnet run - Haltepunkte werden erreicht bei:
- Dem Rendern auf der Serverseite (SSR)
- Den Aufrufen von Server Actions
- Der Kompilierung von Komponenten
Beobachte die Server Actions in den DevTools des Browsers:
- Öffne den Tab Network
- Filtere nach
_equantic/actions - Untersuche:
- Die Nutzlast der Anfrage (Methodenname, Argumente)
- Die Daten der Antwort
- Die Zeitangaben
- Die Fehler (mit Aufrufstapeln)
Wenn du IRequireAssets verwendest, prüfe, ob die Abhängigkeiten korrekt eingespeist werden:
- Quelle untersuchen: öffne im Browser „Seitenquelltext anzeigen“ und suche nach den Script- und Style-Tags.
- Network-Tab: prüfe, ob die externen URLs (z. B. CDNs) erfolgreich laden (Status 200).
- Deduplizierung: prüfe, dass mehrere Komponenten dasselbe Script nicht zweimal eingespeist haben.
- Reihenfolge: Stylesheets sollten für ein korrektes Rendern vor den Scripts erscheinen.
Wenn im anfänglichen HTML Assets fehlen:
- Prüfe, dass die Komponente
IRequireAssetsimplementiert. - Stelle sicher, dass
AddUI()inProgram.csaufgerufen wird. - Prüfe, ob die
AssetCollectiondie Assets während des Renderdurchgangs korrekt sammelt.
Der Reconciler verfolgt im Entwicklungsmodus Performance-Metriken:
// Aktiviert die Performance-Verfolgung
window.__EQ_PERF = true;
// Die Metriken ansehen
console.table(window.__EQ_PERF_DATA);Die Metriken umfassen:
- Renderzeit: wie lange jede Komponente zum Rendern brauchte
- Diff-Zeit: im Reconciler verbrachte Zeit
- DOM-Operationen: Zahl der tatsächlichen DOM-Änderungen
- Ereignis-Listener: Zahl der aktiven Listener
Beobachte die Kompilierzeiten:
dotnet build -v:detailedAchte auf:
- Die Dauer des Targets
CompileEQuanticUI - Die Zahl der kompilierten Komponenten
- Die Zeit für die TypeScript-Erzeugung
- Die Zeit für das Bündeln mit Bun
Symptom: boot() wird nie ausgeführt, GET /_equantic/runtime.js gibt 404 zurück
Lösung: dem Browser antwortet der Server, nicht die Datei in wwwroot/_equantic/: app.MapUI() bildet /_equantic/runtime.js auf das Bundle ab, das die Assembly eQuantic.UI.Server einbettet. Ein 404 kommt entweder von diesem Endpunkt, dessen Antwort dann die Ressourcen benennt, die die Assembly tatsächlich trägt, oder von überhaupt keinem Endpunkt, prüfe also, dass app.MapUI() ausgeführt wird. Die eigene Kopie des Builds, wwwroot/_equantic/runtime.js, sind dieselben Bytes für die Werkzeuge, die die Module außerhalb eines Servers laden; das Target CopyEQuanticRuntime nimmt sie aus dem Server-Paket, und der Build scheitert mit „the served runtime was not found“, wenn es das nicht kann (siehe Paketarchitektur)
# The copy comes from the Server package (not the SDK's)
ls ~/.nuget/packages/equantic.ui.server/<version>/tools/runtime/
# Force rebuild
dotnet clean
dotnet build -v:nSymptom: das auf dem Server gerenderte HTML hat ein Theme, das Rendern auf der Clientseite nicht
Ursache: der Blob der Theme-Brücke wurde beim Start nicht übernommen
Lösung:
- Prüfe, dass runtime.js vor den Skripten der Komponenten lädt
- Suche in der Konsole des Browsers nach
[eQuantic.UI] Boot process started - Untersuche
window.__EQ_THEME__in der Konsole, es sollte das serialisierte Theme enthalten
Symptom: der ursprüngliche C#-Code lässt sich in den DevTools des Browsers nicht debuggen
Lösung:
- Baue in Debug, oder setze
EQuanticSourceMapsauffull: ein Build in jeder anderen Konfiguration schreibt keine Karte, und eineexternal-Karte ist nicht von ihrem Modul verlinkt, also findet der Browser sie nicht von selbst - Prüfe, dass die
.map-Dateien inwwwroot/_equantic/existieren - Aktiviere Source Maps in den Einstellungen der DevTools des Browsers
- Leere den Cache des Browsers und baue neu
- Ein Rahmen, der im TypeScript-Zwischenstand (
obj/eQuantic/ts/…) stehen bleibt, kommt von einer Karte, die eqc nicht zusammensetzen konnte, und eqc meldet jede davon im Build-Protokoll („… leads to the TypeScript only“)
Symptom: Fehler werden in der Konsole protokolliert, aber es gibt keine Überlagerung
Prüfungen:
- Ist
window.__EQ_DEV__true? (In der Konsole prüfen) - Ist die Fehlerüberlagerung importiert? (Prüfen, dass runtime.js sie einschließt)
- Ist das CSS der Fehlerüberlagerung geladen? (Nach Stilen von
#equantic-error-overlaysuchen)
Anzeige erzwingen:
// Manually trigger overlay
import { errorOverlay } from "@equantic/ui-runtime/dev";
errorOverlay.show({ message: "Test error" });- Nutze den Logger freigebig: füge während der Entwicklung Debug-Protokolle hinzu, sie sind in der Produktion kostenlos
-
Teste beide Modi: teste immer mit den Umgebungen
DevelopmentundProduction - Beobachte das Netzwerk: halte den Network-Tab der DevTools offen, um fehlgeschlagene Server Actions zu erwischen
-
Source Maps kommen mit Debug: ein Debug-Build schreibt sie (
EQuanticSourceMapsist dortfull), es gibt also nichts zu aktivieren - Nutze die Fehlerüberlagerung: unterdrücke Fehler nicht, lass sie die Überlagerung zeigen
Für Probleme in der Produktion:
- Serverprotokolle: sieh in den Protokollen von ASP.NET Core nach Fehlern von Server Actions
-
Browser-Konsole: nur
warn- underror-Protokolle erscheinen - Sentry/AppInsights: binde Dienste zur Fehlerverfolgung ein
-
Source Maps: eine Veröffentlichung liefert keine. Baue mit
EQuanticSourceMaps=externalund lade die Karten, die sie inwwwroot/_equantic/schreibt, zu deinem Fehler-Tracker hoch; serviere niemals einefull-Karte aus einem Web-Root, da sie die Quelle der Komponente ist
Bevor du Probleme meldest:
- Sieh in der Konsole des Browsers nach Fehlern
- Prüfe, dass
window.__EQ_DEV__true (Entwicklung) oder false (Produktion) ist - Bestätige, dass runtime.js lädt (Network-Tab)
- Prüfe den Blob der Theme-Brücke (
window.__EQ_THEME__) - Teste mit deaktiviertem Browser-Cache
- Versuche es im Inkognito- oder privaten Modus
- Vergleiche das HTML des SSR mit dem des CSR
- Sieh die Ausgabe von MSBuild nach Warnungen durch
- Prüfe, dass die NuGet-Pakete in den richtigen Versionen vorliegen
- Runtime-Architektur: das Laufzeitsystem verstehen
- Build-Ablauf: wie Kompilierung und Bündelung funktionieren
- Performance: Optimierungstechniken
🏁 Hier starten
🏗️ Architektur
- Architekturüberblick
- Write-once-Komponenten
- Deklarative Oberfläche
- Paketarchitektur
- Komponenten
- Styling
- Lokalisierung
- Analytics und GTM
📱 Write-once
- Photon-Engine
- Design System
- Fähigkeiten
- Speicher
- Formulare
- Code-Editor
- Markdown
- Mermaid
- E-Mail-Rendering
⚙️ Kompilierung
⚡ Runtime
🔌 Server
🎨 Ökosystem
🚀 Entwicklung