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.kind 和 event.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。