頁面片段

本頁內容

page:fragments 鉤子允許外掛程式向公開頁面貢獻原始 HTML、腳本或樣式表。這是分析標籤、第三方小工具、自訂 CSS 以及其他需要將 JavaScript 或標記直接傳送到訪問者瀏覽器的工具的正確選擇。

它僅限於原生外掛程式,因為其輸出作為第一方程式碼在瀏覽器中執行,不受任何沙箱邊界限制。如果您只需要貢獻結構化中繼資料 — meta 標籤、OpenGraph、JSON-LD、允許的 <link> rels — 請改用 page:metadata,它適用於沙箱外掛程式和原生外掛程式。參見 Hooks: page:metadata

Capability

page:fragments 需要 hooks.page-fragments:register capability:

return definePlugin({
	id: "analytics-gtm",
	version: "1.0.0",
	capabilities: ["hooks.page-fragments:register"],
	// ...
});

該 capability 也必須出現在描述符中。

片段在哪裡渲染

範本透過包含來自 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"

範例

外部腳本

以下鉤子將第三方標籤管理器注入 <head>

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

多個片段

鉤子可以返回陣列以一次貢獻多個片段。以下鉤子同時新增腳本和 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.kindevent.page.pageType 來決定是否在給定頁面上貢獻 — 例如,在管理員預覽時跳過分析,或僅在部落格文章上注入 JSON-LD。

何時改用 page:metadata

如果您實際需要的是:

  • meta 描述、robots 指令或 Twitter 卡片 → 使用 kind: "meta"page:metadata
  • OpenGraph 屬性 → 使用 kind: "property"page:metadata
  • 規範或備用 <link> → 使用 kind: "link"page:metadata
  • JSON-LD 圖 → 使用 kind: "jsonld"page:metadata

page:metadata 適用於沙箱外掛程式,免費獲得驗證和去重,並避免向訪問者傳送原始 HTML 的信任負擔。僅在需要傳送 JavaScript 或 HTML 時才使用 page:fragments