頁面片段

本頁內容

page:fragments 掛鉤會向已渲染的公開頁面的 head 或 body 提供腳本或原始 HTML。用它處理結構化中繼資料無法表達的瀏覽器程式碼,例如帶 <noscript> 回退的分析載入器。

片段輸出會作為頁面的第一方程式碼執行。EmDash 僅對受信任的行程內外掛呼叫此掛鉤;沙箱外掛從不貢獻片段。若頁面需要 meta 標籤、canonical 或 alternate 連結、或 JSON-LD,請改用相容沙箱的 page:metadata 掛鉤。

宣告能力與掛鉤

page:fragments 需要原生執行階段定義中的 hooks.page-fragments:register。下面的掛鉤僅在存在已設定的分析 ID 時新增外部腳本與 HTML 回退:

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

原始 HTML 貢獻會按原文插入。盡可能保持片段為靜態;否則請跳脫任何可能來自設定、內容、請求資料或外部服務的值。

對於原生外掛,在 definePlugin() 中宣告能力。描述子不需要第二份副本,因為 createPlugin() 會提供執行階段能力清單。

若缺少能力,EmDash 會記錄警告且不註冊掛鉤。掛鉤錯誤會被記錄,不會阻止頁面渲染。

新增版面插入點

宿主主題選擇支援哪些 placement。它必須將同一 PublicPageContext 傳給對應的 EmDash 元件:

  1. 在版面中建構一次頁面脈絡。

    ---
    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. 在文件的相應位置渲染每個受支援的 placement。

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

EmDashHead 渲染 head 片段以及 EmDash 與外掛中繼資料。EmDashBodyStart 在開始的 <body> 之後立即渲染 body:start 片段,EmDashBodyEnd 在頁面內容之後渲染 body:end 片段。省略某個元件的主題不會渲染該 placement 的片段。請在外掛 README 中記錄任何必要的插入點。

貢獻參考

掛鉤可以回傳一個貢獻、一個陣列或 null。支援的貢獻形式如下:

kind必填欄位選用欄位輸出行為
external-scriptplacement, srcasync, defer, attributes, key渲染 <script src="…"> 元素。
inline-scriptplacement, codeattributes, key在 <script> 元素內渲染 code。
htmlplacement, htmlkey插入 html 且不進行淨化。

placement 接受 head、body:start 或 body:end。

EmDash 會對屬性名稱與值做 HTML 跳脫,並移除名稱以 on 開頭的屬性。對於內聯腳本,會跳脫 </,使值無法關閉 script 元素。這些渲染器檢查不會讓不受信任的 HTML 或 JavaScript 變得安全;外掛仍須依插入的語言與脈絡對插值資料進行編碼。

下面的掛鉤將 JSON 值安全地放入內聯腳本:

"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",
	};
},

在同一 placement 內,具有相同 key 的貢獻保留第一個值。無 key 的外部腳本也會依 src 去重。當兩個掛鉤可能描述同一邏輯片段時,請使用穩定的 key。

頁面脈絡參考

掛鉤接收 { page }。page 物件來自宿主版面,對於請求期間載入的內容項目,可疊加 SEO 面板的選用值。

欄位類型與含義
url絕對頁面 URL。
pathURL 的 pathname。
locale作用中語言地區,或 null。
kindEmDash 項目為 content,其他頁面為 custom。
pageType主題定義的類型,通常為 article 或 website。
title, pageTitle完整文件標題與選用的僅頁面標題。
description, canonical, image頁面中繼資料值,均可為 null。
content已渲染項目的選用 { collection, id, slug } 參照。
seoOpen Graph 標題、描述、圖片與 robots 的選用覆寫。
articleMeta選用的發佈時間、修改時間與作者。
siteName, siteUrl選用的公開網站身分與 origin。
breadcrumbs選用的 root-first { name, url } 項。空陣列表示明確沒有麵包屑。

使用 kind、pageType、path、locale 或 content 限制片段出現的位置。當頁面脈絡已攜帶決策所需的身分時,不要再次取得項目。

盡可能優先使用結構化中繼資料

對以下輸出使用 page:metadata:

  • <meta name="…"> 與 <meta property="…"> 標籤
  • canonical、alternate、author、license、nlweb 與 site.standard.document 連結
  • JSON-LD 圖

page:metadata 會驗證其結構化貢獻,與頁面基礎中繼資料去重,並同時適用於原生與沙箱外掛。當瀏覽器需要接收結構化掛鉤無法表示的可執行程式碼或標記時,使用 page:fragments。