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 元件:
-
在版面中建構一次頁面脈絡。
--- 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, }); --- -
在文件的相應位置渲染每個受支援的 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-script | placement, src | async, defer, attributes, key | 渲染 <script src="…"> 元素。 |
inline-script | placement, code | attributes, key | 在 <script> 元素內渲染 code。 |
html | placement, html | key | 插入 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。 |
path | URL 的 pathname。 |
locale | 作用中語言地區,或 null。 |
kind | EmDash 項目為 content,其他頁面為 custom。 |
pageType | 主題定義的類型,通常為 article 或 website。 |
title, pageTitle | 完整文件標題與選用的僅頁面標題。 |
description, canonical, image | 頁面中繼資料值,均可為 null。 |
content | 已渲染項目的選用 { collection, id, slug } 參照。 |
seo | Open 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。