page:fragments 鉤子允許外掛向公開頁面貢獻原始 HTML、腳本或樣式表。這是新增分析標籤、第三方小工具、自訂 CSS 以及任何需要直接向訪客瀏覽器提供 JavaScript 或標記的內容的正確工具。
它僅限於原生外掛,因為其輸出作為第一方程式碼在瀏覽器中執行,不受任何沙箱邊界限制。如果你只需要貢獻結構化中繼資料 — meta 標籤、OpenGraph、JSON-LD、白名單中的 <link> rels — 請改用 page:metadata,它對沙箱和原生外掛都可用。參見 Hooks: page:metadata。
能力
page:fragments 需要 hooks.page-fragments:register 能力:
return definePlugin({
id: "analytics-gtm",
version: "1.0.0",
capabilities: ["hooks.page-fragments:register"],
// ...
});
該能力還必須出現在描述符中。
片段渲染位置
模板透過包含來自 emdash/ui 的相關元件來選擇接收片段:
<EmDashHead />— 渲染placement: "head"的片段以及所有page:metadata貢獻。<EmDashBodyStart />— 渲染placement: "body:start"的片段。<EmDashBodyEnd />— 渲染placement: "body:end"的片段。
省略其中一個元件的模板會靜默忽略針對該位置的片段 — 你的外掛不會中斷,片段只是不會出現。請在外掛的 README 中記錄你的位置要求。
貢獻類型
三種貢獻類型:
type PageFragmentContribution =
| {
kind: "external-script";
placement: PagePlacement;
src: string;
async?: boolean;
defer?: boolean;
attributes?: Record<string, string>;
key?: string;
}
| {
kind: "inline-script";
placement: PagePlacement;
code: string;
attributes?: Record<string, string>;
key?: string;
}
| {
kind: "html";
placement: PagePlacement;
html: string;
key?: string;
};
PagePlacement 是 "head" | "body:start" | "body:end"。
範例
外部腳本
注入第三方標籤管理器:
"page:fragments": async (event, ctx) => {
const containerId = await ctx.kv.get<string>("settings:gtmContainerId");
if (!containerId) return null;
return {
kind: "external-script",
placement: "head",
src: `https://www.googletagmanager.com/gtm.js?id=${containerId}`,
async: true,
};
},
內聯腳本
在 <body> 頂部執行一小段 JavaScript:
"page:fragments": async (event, ctx) => {
if (event.page.kind !== "content") return null;
return {
kind: "inline-script",
placement: "body:start",
code: `window.contentId = ${JSON.stringify(event.page.content?.id)};`,
};
},
HTML 片段
在 <body> 末尾附加一個 noscript 回退:
"page:fragments": async (event, ctx) => {
const containerId = await ctx.kv.get<string>("settings:gtmContainerId");
if (!containerId) return null;
return {
kind: "html",
placement: "body:end",
html: `<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=${containerId}" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>`,
};
},
多個片段
從單個鉤子返回陣列以貢獻多個片段:
"page:fragments": async (event, ctx) => {
const id = await ctx.kv.get<string>("settings:gtmContainerId");
if (!id) return null;
return [
{
kind: "external-script",
placement: "head",
src: `https://www.googletagmanager.com/gtm.js?id=${id}`,
async: true,
},
{
kind: "html",
placement: "body:end",
html: `<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=${id}" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>`,
},
];
},
頁面事件
page:fragments 鉤子接收與 page:metadata 相同的事件形狀:
{
page: {
url: string;
path: string;
locale: string | null;
kind: "content" | "custom";
pageType: string;
title: string | null;
pageTitle?: string | null;
description: string | null;
canonical: string | null;
image: string | null;
content?: { collection: string; id: string; slug: string | null };
}
}
使用 event.page.kind 和 event.page.pageType 來決定是否在給定頁面上貢獻 — 例如,在管理預覽上跳過分析,或僅在部落格文章上注入 JSON-LD。
何時改用 page:metadata
如果你實際需要的是:
- meta 描述、robots 指令或 Twitter 卡片 → 使用
page:metadata並設定kind: "meta"。 - OpenGraph 屬性 → 使用
page:metadata並設定kind: "property"。 - canonical 或 alternate
<link>→ 使用page:metadata並設定kind: "link"。 - JSON-LD 圖 → 使用
page:metadata並設定kind: "jsonld"。
page:metadata 在沙箱外掛中有效,免費獲得驗證和去重,並避免向訪客提供原始 HTML 的信任負擔。僅在真正需要提供 JavaScript 或 HTML 時才使用 page:fragments。