在 App Router 中注入 JSON-LD,最可靠的方式是在 Server Component 里直接输出一段 `<script type="application/ld+json">`。这样,结构化数据会出现在服务器返回的初始 HTML 中,AI 爬虫无需执行 JavaScript 就能读取。至于通过 useEffect 或 Google Tag Manager 后置注入的 JSON-LD,对答案引擎而言几乎等于不存在——多数爬虫不会等页面完成 hydration,就已经抓取完毕并离开。
为什么必须由服务器端输出?
关键在于输出时机。Server Component 会在请求发生时把 JSON-LD 写进 HTML 字符串,因此 GPTBot、ClaudeBot、PerplexityBot、Google-Extended 等爬虫首次抓取到的文件就包含完整 Schema。客户端注入则必须等待浏览器下载并执行 bundle,而多数 AI 爬虫根本不会执行这一步。这里还有一个很容易误判的陷阱:Google Rich Results Test 会替你渲染 JavaScript,所以客户端注入的数据在测试工具中看起来一切正常,让人误以为已经通过验证;但答案引擎实际读取的往往是未经渲染的原始 HTML,其中根本没有 Schema。我们为客户开展技术审计时,这是最常见、也最容易被忽略的失分项。
最小可行示例:一个 page.tsx 页面
实现代码本身很短,不需要额外安装包,也不需要创建 API 路由,直接在负责渲染文章的 Server Component 中完成即可。核心只有三件事:获取数据、组装对象、输出脚本。
- 在 `app/blog/[slug]/page.tsx` 中(默认就是 Server Component),先通过 `await` 获取当前文章的数据。
- 组装一个纯 JavaScript 对象 `jsonLd`,开头写入 `"@context": "https://schema.org"` 和 `"@type": "Article"`,其余字段(headline、datePublished、author、image)则根据文章数据动态填充。
- 把它放在返回 JSX 的前部:`<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd).replace(/</g, "\\u003c") }} />`。
- 不要导入任何客户端库,也不要在文件顶部添加 `"use client"`——一旦添加,它就会变成客户端组件,前面的处理也就失去意义。
用 @graph 串联多个实体
一个页面通常不只需要一种 Schema:品牌主体对应 Organization,文章对应 Article,导航路径对应 BreadcrumbList,问答区域对应 FAQPage。与其输出四个彼此独立的 script 标签,更清晰的做法是用 `@graph` 数组把它们放进同一个 JSON-LD 对象,再通过 `@id` 建立相互指向的关系。例如,让 Article 的 `publisher` 指向 Organization 的 `@id`,再让 BreadcrumbList 的最后一项指向当前页面的 `@id`。这样,答案引擎一次解析就能获得完整的实体关系图,也更不容易抓取到相互矛盾或重复的节点。
- Organization 和 WebSite:放在 `app/layout.tsx` 中,全站共用一次即可,不要在每个页面重复声明。
- Article 或 BlogPosting:放在文章的 `page.tsx` 中,各字段根据当前页面的数据动态填充。
- BreadcrumbList:按照路由层级组装,尤其有助于 AI 理解网站结构。
- FAQPage:只有页面上确实存在问答区域时才添加,而且 `acceptedAnswer` 的文本必须与页面内容一致。不要为了凑 Schema 而虚构问答。

让 JSON-LD 易于维护
如果把 Schema 对象散落在各个页面里,三个月后往往就没人敢改。我们的做法是抽出一个 `lib/schema.ts`,并使用 `schema-dts` 类型(`import type { Article, WithContext } from "schema-dts"`)约束每个生成函数的返回值。字段名或类型不正确时,TypeScript 会在编译阶段报错。页面只需调用 `buildArticleSchema(post)`,拿到类型安全的对象后再输出。以后只改一处,就能让全站同步生效。URL 始终使用绝对路径,并统一收口到 `SITE_URL` 常量中,避免正式网站与预览域名之间的 `@id` 对不上。
常见错误与上线前验证
- JSON-LD 中的 `url`、`image`、`datePublished` 与页面实际内容不一致——答案引擎一旦发现偏差,就会降低对整个页面的信任。
- 使用相对路径作为 `@id` 或 `url`,切换到预览域名后就可能指向错误位置。
- 忘记转义 `<`,或误在页面顶部添加了 `"use client"`,导致脚本变成客户端输出。
- 上线前,先用 Google Rich Results Test 检查语法,再用 `curl` 直接抓取页面的原始 HTML,确认脚本确实包含在初始响应中,而不是只出现在浏览器 DevTools 里。这一步最关键,因为它准确模拟了 AI 爬虫实际看到的内容。
在我们审计过的网站中,大多数 JSON-LD 问题并不是 Schema 写错了,而是它只存在于客户端。服务器返回的 HTML 中没有这些数据,AI 就会把它视为不存在。— Tenten GEO Technical Audit
从技术实现走向 AI 引用
把 JSON-LD 移到 Server Component,只是让内容达到“机器可读”的最低门槛。下一步还要统一实体命名、内部链接和跨页面信息,AI 才能稳定地把你的网站作为可信来源引用。如果你不确定网站的结构化数据是否已经进入爬虫看到的 HTML,可以预约一次 30 分钟的 GEO 诊断。我们会直接抓取源代码,帮你梳理缺口,而不是只看渲染后的页面。



