Seitenfragmente

Auf dieser Seite

Der Hook page:fragments liefert Skripte oder Roh-HTML an den Head oder Body einer gerenderten öffentlichen Seite. Nutzen Sie ihn für Browsercode, den strukturierte Metadaten nicht ausdrücken können, etwa einen Analytics-Loader mit <noscript>-Fallback.

Fragmentausgabe läuft als First-Party-Seitencode. EmDash ruft diesen Hook nur für vertrauenswürdige In-Process-Plugins auf; Sandboxed Plugins tragen nie Fragmente bei. Wenn die Seite Meta-Tags, kanonische oder alternate Links oder JSON-LD braucht, verwenden Sie stattdessen den sandbox-kompatiblen page:metadata-Hook.

Capability und Hook deklarieren

page:fragments erfordert hooks.page-fragments:register in der nativen Runtime-Definition. Der folgende Hook fügt ein externes Skript und einen HTML-Fallback nur hinzu, wenn eine konfigurierte Analytics-ID vorhanden ist:

return definePlugin({
	id: "plugin-analytics",
	version: "0.1.0",
	capabilities: ["hooks.page-fragments:register"],
	hooks: {
		"page:fragments": async (event, ctx) => {
			const analyticsId = await ctx.settings.get<string>("analyticsId");
			if (!analyticsId || event.page.path.startsWith("/_emdash/")) return null;

			const encodedId = encodeURIComponent(analyticsId);
			return [
				{
					kind: "external-script",
					placement: "head",
					src: `https://analytics.example.com/client.js?id=${encodedId}`,
					async: true,
					key: "analytics-client",
				},
				{
					kind: "html",
					placement: "body:end",
					html: "<noscript>Analytics requires JavaScript.</noscript>",
					key: "analytics-fallback",
				},
			];
		},
	},
});

Roh-HTML-Beiträge werden unverändert eingefügt. Halten Sie das Fragment möglichst statisch; andernfalls escapen Sie jeden Wert, der aus Einstellungen, Inhalt, Anfragedaten oder einem externen Dienst stammen kann.

Für native Plugins deklarieren Sie die Capability in definePlugin(). Der Deskriptor braucht keine zweite Kopie, weil createPlugin() die Runtime-Capability-Liste liefert.

Fehlt die Capability, protokolliert EmDash eine Warnung und registriert den Hook nicht. Hook-Fehler werden protokolliert und verhindern nicht das Rendern der Seite.

Layout-Einfügepunkte hinzufügen

Das Host-Theme wählt, welche Placements es unterstützt. Es muss denselben PublicPageContext an die entsprechenden EmDash-Komponenten übergeben:

  1. Bauen Sie den Seitenkontext einmal im Layout.

    ---
    import { createPublicPageContext } from "emdash/page";
    import {
        EmDashBodyEnd,
        EmDashBodyStart,
        EmDashHead,
    } from "emdash/ui";
    
    interface Props {
        title: string;
        description?: string;
        content?: { collection: string; id: string; slug?: string | null };
    }
    
    const { title, description, content } = Astro.props;
    const page = createPublicPageContext({
        Astro,
        kind: content ? "content" : "custom",
        pageType: content ? "article" : "website",
        title,
        description,
        content,
    });
    ---
  2. Rendern Sie jedes unterstützte Placement im passenden Teil des Dokuments.

    <html lang="en">
        <head>
            <title>{title}</title>
            <EmDashHead page={page} />
        </head>
        <body>
            <EmDashBodyStart page={page} />
            <slot />
            <EmDashBodyEnd page={page} />
        </body>
    </html>

EmDashHead rendert head-Fragmente sowie EmDash- und Plugin-Metadaten. EmDashBodyStart rendert body:start-Fragmente direkt nach dem öffnenden <body>, und EmDashBodyEnd rendert body:end-Fragmente nach dem Seiteninhalt. Ein Theme, das eine Komponente weglässt, rendert keine Fragmente für dieses Placement. Dokumentieren Sie jeden erforderlichen Einfügepunkt in der Plugin-README.

Beitragsreferenz

Ein Hook kann einen Beitrag, ein Array oder null zurückgeben. Die unterstützten Beitragsformen sind:

kindPflichtfelderOptionale FelderAusgabehandlung
external-scriptplacement, srcasync, defer, attributes, keyRendert ein <script src="…">-Element.
inline-scriptplacement, codeattributes, keyRendert code in einem <script>-Element.
htmlplacement, htmlkeyFügt html ohne Bereinigung ein.

placement akzeptiert head, body:start oder body:end.

EmDash HTML-escapet Attributnamen und -werte und entfernt Attribute, deren Namen mit on beginnen. Bei Inline-Skripten escapet es </, damit ein Wert das Skriptelement nicht schließen kann. Diese Renderer-Prüfungen machen unvertrauenswürdiges HTML oder JavaScript nicht sicher; das Plugin muss interpolierte Daten weiterhin für die Sprache und den Kontext kodieren, in dem sie eingefügt werden.

Der folgende Hook platziert sicher einen JSON-Wert in einem Inline-Skript:

"page:fragments": async (event) => {
	if (event.page.kind !== "content" || !event.page.content) return null;

	return {
		kind: "inline-script",
		placement: "body:start",
		code: `window.currentContent = ${JSON.stringify({
			collection: event.page.content.collection,
			id: event.page.content.id,
		})};`,
		key: "current-content",
	};
},

Innerhalb eines Placements behalten Beiträge mit demselben key den ersten Wert. Externe Skripte ohne Schlüssel werden auch nach src dedupliziert. Verwenden Sie einen stabilen Schlüssel, wenn zwei Hooks dasselbe logische Fragment beschreiben könnten.

Seitenkontext-Referenz

Der Hook empfängt { page }. Das Seitenobjekt kommt aus dem Host-Layout, mit optionalen SEO-Panel-Werten, die für einen während der Anfrage geladenen Inhaltseintrag darübergelegt werden.

FeldTyp und Bedeutung
urlAbsolute Seiten-URL.
pathURL-Pfadname.
localeAktive Locale oder null.
kindcontent für einen EmDash-Eintrag oder custom für eine andere Seite.
pageTypeTheme-definierter Typ, üblicherweise article oder website.
title, pageTitleVollständiger Dokumenttitel und optionaler seitenbezogener Titel.
description, canonical, imageSeitenmetadatenwerte, jeweils nullable.
contentOptionale Referenz { collection, id, slug } für den gerenderten Eintrag.
seoOptionale Open-Graph-Titel-, Beschreibungs-, Bild- und robots-Überschreibungen.
articleMetaOptionale Veröffentlichungszeit, Änderungszeit und Autor.
siteName, siteUrlOptionale öffentliche Site-Identität und Origin.
breadcrumbsOptionale root-first-Elemente { name, url }. Ein leeres Array bedeutet ausdrücklich keine Breadcrumbs.

Nutzen Sie kind, pageType, path, locale oder content, um einzuschränken, wo ein Fragment erscheint. Laden Sie keinen Eintrag erneut, wenn der Seitenkontext die für die Entscheidung nötige Identität bereits trägt.

Strukturierte Metadaten bevorzugen, wenn möglich

Verwenden Sie page:metadata für folgende Ausgaben:

  • Tags <meta name="…"> und <meta property="…">
  • kanonische, alternate, author, license, nlweb und site.standard.document Links
  • JSON-LD-Graphen

page:metadata validiert seine strukturierten Beiträge, dedupliziert sie mit den Basismetadaten der Seite und funktioniert in nativen und Sandboxed Plugins. Verwenden Sie page:fragments, wenn der Browser ausführbaren Code oder Markup erhalten muss, das der strukturierte Hook nicht darstellen kann.