Analytik-Ereignisse senden
Ein Callback, fünf Ereignisse, kein Anbieter — verdrahte duxts Leserinteraktionen mit der Analytik, die du ohnehin betreibst.
duxt meldet, was Leser mit einer Dokumentationsseite tun — suchen, einen Befehl kopieren, einen Endpoint ausprobieren, „War diese Seite hilfreich?“ beantworten — an eine einzige Funktion, die du schreibst. Sie liefert keinen Anbieter, kein SDK und kein Skript aus, und eine Seite, die diese Funktion nicht geschrieben hat, meldet nichts und lädt nichts zusätzlich herunter.
Es gibt kein Standardziel, denn eine Dokumentationsebene kann keines für die
Leser eines anderen aussuchen. Ohne duxt.analytics.track erreicht jede
Aufrufstelle unten eine fehlende Funktion und hält an.
Schritte
- Schreibe die Callback-Funktion. Sie lebt in
app.config.ts, neben dem Rest deiner duxt-Konfiguration, und bekommt ein Ereignis pro Interaktion übergeben:export default defineAppConfig({ duxt: { analytics: { track(event) { // `event.name` ist der Diskriminator; `event.context` sagt, auf // welcher Seite, in welcher Locale, aus welcher Quelle und in // welcher Version es passiert ist. console.log(event.name, event.context.path); } } } });
Die Callback-Funktion läuft nur im Browser. Ein Build und ein Vorab-Rendering rufen sie nie auf, ein statisches Rendering füllt deine Analytik also nicht mit Seiten, die ein Crawler geöffnet hat. - Sende es dorthin, wo deine Analytik ohnehin liegt. Was auch immer dein
Anbieter anbietet — ein SDK, das ohnehin auf der Seite liegt, oder eine
eigene Route, damit der Browser des Lesers nie mit einem Dritten spricht,
den du nicht gewählt hast:
export default defineAppConfig({ duxt: { analytics: { track(event) { const { name, context, ...properties } = event; // Dein Anbieter, deine Eigenschaftsnamen, deine // Einwilligungsschranke. window.plausible?.(name, { props: { ...context, ...properties } }); } } } });
duxt setzt kein Cookie, speichert nichts, puffert nichts und zeigt kein
Einwilligungsbanner — es hat nichts zu fragen, denn nichts verlässt die Seite
außer durch deine Funktion. Das heißt auch: Die Schranke gehört dir.
Wenn deine Datenschutzerklärung eine Einwilligung verlangt, bevor das einen
Anbieter erreicht, prüfe sie in track und kehre früh zurück.
- Wähle die Ereignisse, die du willst, und lass den Rest fallen. Die Union
ist geschlossen, ein
switchdeckt sie also erschöpfend ab, und was du nicht nennst, wird schlicht nicht gesendet:
track(event) {
if (event.name === 'search' && event.results === 0) {
// Der eine Bericht, den eine Dokumentationsseite fast immer will: ein
// Begriff, nach dem jemand gesucht hat und den die Seite nicht
// beantworten konnte.
report('docs-search-miss', { query: event.query, ...event.context });
}
}
- Prüfe, dass ein Ereignis ankommt, mit dem Kontext, den du erwartet hast — unter einem Versions- und Locale-Präfix, nicht nur an der nackten URL.
Die Ereignisse
Jedes Ereignis hat einen name, eigene Felder und einen context.
name | Wird gesendet, wenn | Trägt |
|---|---|---|
search | Eine entprellte, nicht leere Suchanfrage zur Ruhe kommt — auch eine, die nichts gefunden hat | query, results, approximate |
search-result | Ein Leser einen Treffer öffnet | query, rank, to, collection |
copy | Ein Codebeispiel der Ebene kopiert wird | kind, language, manager, sample |
api-request | Eine Try-it-Anfrage zur Ruhe kommt | operation, method, path, outcome, statusClass, duration |
feedback | Ein Leser „War diese Seite hilfreich?“ beantwortet | helpful |
search feuert einmal pro abgeschlossener Suchanfrage, nicht pro
Tastenanschlag: Die Entprellung verwirft, was niemand zu Ende getippt hat, und
ein Ergebnis, das ein neueres ablöst, wird nie gemeldet, weil der Leser es nie
gesehen hat.
copy deckt die eigenen Codebeispiele der Ebene ab — einen Codeblock, einen
::code-group-Block, einen ::package-managers-Block, ein Request-Sample im
Try-it-Client — und kind sagt, welches davon. „Seite als Markdown kopieren“
gehört nicht zu dieser Familie und sendet nichts: Es ist die Seite, kein
Beispiel darin.
api-request beschreibt die Operation so, wie das OpenAPI-Dokument sie
deklariert. path ist die Vorlage (/pets/{id}), nie die URL, die aufgerufen
wurde.
Der Kontext
| Feld | Hinweise |
|---|---|
path | Der Dokumentationspfad, ohne Locale-Präfix, Query-String oder Fragment |
locale | Die Locale, in der die Seite gelesen wurde |
source | Aus welcher Quelle die Seite stammt — fehlt auf einer Seite mit nur einer Quelle |
version | Welche Version — fehlt, wo die Quelle nur eine hat |
Was nie gesendet wird
Diese Liste ist der Sinn der Funktion, deshalb lohnt es sich, sie deutlich auszusprechen. Kein Ereignis trägt:
- ein API-Credential, einen Header oder ein Token;
- einen Request- oder Response-Body oder eine konkrete Request-URL;
- den Text, der kopiert wurde;
- den gerenderten Inhalt eines Suchtreffers;
- irgendeine Browser-, Sitzungs- oder Leserkennung;
- den Query-String oder das Fragment aus dem Pfad der Seite selbst.
duxt überträgt, puffert und speichert auch selbst nie etwas. Es gibt in der Ebene keine Warteschlange, keinen Wiederholungsversuch, keinen Speicher und keinen Netzwerkaufruf: Deine Funktion ist das Einzige, was die Seite je verlässt.
Deine Callback-Funktion wird nie abgewartet, und was sie wirft oder womit sie ablehnt, wird verschluckt — ein Anbieter, der ausfällt, darf nicht die Suche, das Kopieren, das Feedback oder die Anfrage mit sich reißen.
Seiten-Feedback, so oder so
feedback ist dieselbe Antwort, die DuxtPageFeedback schon immer gesendet
hat. Das @feedback-Ereignis der Komponente und ihr Standard-Slot sind
unverändert, sie zu umhüllen funktioniert also
weiterhin, und keiner der beiden Wege braucht den anderen — nimm das
Analytik-Callback, wenn du ein Ziel für alles willst, und die Hülle, wenn das
Feedback allein irgendwo eigenes landen soll.
Checkliste
-
duxt.analytics.trackist gesetzt, und mehr war nicht nötig, um es einzuschalten - Das Ziel gehört dir — ein Anbieter deiner Wahl oder eine eigene Route
- Die Einwilligung wird in
trackgeprüft, falls deine Richtlinie danach fragt - Ein Ereignis kam mit dem Pfad, der Locale, der Quelle und der Version an, die du erwartet hast
- Nichts in dem, was du weiterleitest, identifiziert den Leser