动态元数据生成:让机器读懂你的页面
元数据决定可见性
你建了一个网站。内容很好,功能完善。但在搜索结果里,它显示为"无标题"。在社交平台分享时,没有预览图,只有一个干瘪的链接。
问题不在内容本身,而在机器看不懂你在说什么。
搜索引擎爬虫、社交平台的爬虫、AI 系统,它们都需要结构化的元数据来理解页面。标题是什么?描述是什么?这个页面有其他语言版本吗?作者是谁?
在 Next.js 中,generateMetadata 函数就是你和这些机器对话的接口。
动态生成的必要性
元数据可以写死在 HTML 里。但当你有多语言支持、动态路由、个性化内容时,写死就不够了。
一个博客有几十篇文章,每篇文章的标题、描述、作者都不同。一个国际化网站有五种语言,每种语言的元数据都要单独配置。一个电商网站有成千上万个商品页,每个商品的 OpenGraph 图片都不一样。
动态生成元数据让你在服务端根据请求参数(路由、语言、用户状态)生成对应的元数据。
export async function generateMetadata({
params: { locale }
}: {
params: { locale: string }
}): Promise<Metadata> {
const localeMetadata = metadata[locale];
return {
title: localeMetadata.title,
description: localeMetadata.description,
keywords: localeMetadata.keywords,
metadataBase: new URL('https://example.com'),
alternates: {
canonical: `/${locale}`,
languages: {
'en': '/en',
'zh-CN': '/zh-CN',
'ja': '/ja',
}
},
openGraph: {
title: localeMetadata.title,
description: localeMetadata.description,
url: `https://example.com/${locale}`,
images: ['/og-image.jpg'],
locale: locale,
type: 'website',
},
twitter: {
card: 'summary_large_image',
images: ['/twitter-card.jpg'],
},
};
}
这段代码在每次请求时执行,根据当前语言环境生成对应的元数据。用户访问 /en,看到英文标题。访问 /zh-CN,看到中文标题。搜索引擎抓取不同路径,索引不同语言版本。
关键字段的作用
title 和 description 是最基础的。它们出现在搜索结果的标题和摘要里,也是用户决定是否点击的第一印象。
metadataBase 定义了基础 URL。当你在 openGraph.images 里写 ['/og-image.jpg'] 时,Next.js 会把它解析成 https://example.com/og-image.jpg。没有 metadataBase,相对路径无法正确解析。
alternates 告诉搜索引擎这个页面有其他语言版本。canonical 指向标准 URL,避免重复内容问题。languages 列出所有语言及其路径,让 Google 为不同地区的用户显示对应语言的版本。
openGraph 和 twitter 决定了社交分享时的预览效果。当有人把你的页面链接分享到 Facebook、LinkedIn、Twitter 时,平台会抓取这些元数据,生成带图片、标题、描述的预览卡片。没有这些字段,分享链接只会显示为纯文本 URL。
robots 控制搜索引擎行为。index: true 表示允许索引,follow: true 表示允许跟踪页面内的链接。如果你有后台管理页面、隐私内容、重复页面,可以设置 index: false 来避免被搜索引擎收录。
page.tsx 对应一个具体路由,比如 /blog/[slug] 对应一篇文章。每个页面可以有自己的 generateMetadata,根据动态参数生成元数据。
layout.tsx 是布局模板,可以被多个页面共享。layout 也可以有 generateMetadata,但它生成的元数据会被 page 的元数据覆盖(按照 Next.js 的元数据合并规则)。
通常的做法是:
- 在 layout 里设置通用元数据(网站名称、默认描述、favicon)
- 在 page 里设置特定于该页面的元数据(标题、描述、OpenGraph 图片)
GEO(生成式引擎优化)关注的是 AI 系统如何检索、理解、引用你的内容。
title 和 description 依然重要,因为 AI 的信息来源仍然依赖搜索引擎。你在 Google 上找不到,AI 也很难找到你。
但 GEO 更看重内容的结构化程度和事实的清晰陈述。AI 不会欣赏修辞,它需要能直接提取的信息。元数据提供了第一层结构,但页面内容的语义标签(如 schema.org)、明确的事实陈述、权威性引用更为关键。
参考 ai-discoverability 了解 GEO 与传统 SEO 的区别。
hreflang 是 alternates.languages 在 HTML 中对应的标签,告诉搜索引擎页面有其他语言版本。
为什么需要?
- 避免重复内容惩罚:如果你的英文页面和中文页面内容相似,Google 可能认为是重复内容。hreflang 明确告诉它们是同一内容的不同语言版本。
- 提升用户体验:Google 会根据用户的语言偏好和地理位置,在搜索结果中显示对应语言的版本。
即使有 AI 翻译,多语言仍然必要吗?
是的。原因有二:
第一,主动权在你手里。你可以精确控制每种语言的表达方式、文化适配、关键词优化。AI 翻译可能准确,但无法替代人工对市场的理解。
第二,搜索引擎和社交平台依然按语言分发内容。你的英文页面很难出现在中文用户的搜索结果里,即使 AI 能翻译它。主动提供多语言版本,意味着你在每个语言市场都有独立的存在。
robots.index 和 robots.follow 控制搜索引擎爬虫的行为:
index: true→ 允许在搜索结果中显示此页面index: false→ 不在搜索结果中显示,但仍可能被抓取follow: true→ 允许爬虫跟踪页面内的链接follow: false→ 不跟踪页面内的链接
对于 LLM 爬虫(如 GPTBot、ClaudeBot),情况更复杂。它们有自己的爬虫规则,部分遵守 robots.txt,但不一定遵守页面级的 robots meta 标签。
目前控制 LLM 爬取的方式:
- 在
robots.txt中禁止特定爬虫(如User-agent: GPTBot) - 使用 HTTP 头
X-Robots-Tag - 某些平台提供专门的 AI 爬虫控制(如 Cloudflare 的 AI 爬虫阻止功能)
当你需要 AI 生成 favicon 和 PWA 配置时,提供以下信息会更高效:
Favicon:
- 品牌主色调(如
#3B82F6) - 图标风格(极简/扁平/写实)
- 是否需要多尺寸版本(16x16, 32x32, 180x180 等)
PWA 清单:
- 应用名称(短名称和完整名称)
- 应用描述
- 主题颜色和背景颜色
- 图标路径和尺寸
- 启动 URL
- 显示模式(standalone / fullscreen / minimal-ui)
文件位置通常是 public/favicon/ 和 public/manifest.json。
元数据是承诺
元数据不只是技术配置。它是你对外界的承诺。
你在 description 里写什么,就告诉搜索引擎这个页面是关于什么的。你在 openGraph.image 里放什么图,就决定了别人分享你的链接时会看到什么。
这些信息会被复制、转发、索引、引用。它们的生命周期远远超过页面本身。
所以动态生成元数据时,不只是填写字段。而是要问:这个页面的本质是什么?我想让机器怎么理解它?我想让看到分享链接的人得到什么印象?
元数据是你和机器的对话,也是你和未来读者的第一次见面。
Related Posts
Articles you might also find interesting
用静态导出控制视口
Next.js 中的视口配置通过静态导出模式定义页面初始状态,理解其背后的设计约束能够更好地控制用户体验边界。
从底部开始:内容策略的逆向构建法
大多数人从顶部开始,创建广泛的认知内容。但真正高效的策略是从底部开始,从那些已经准备好转化的用户开始。这是一个关于逆向思维的实践方法。
GEO 不是要替代传统 SEO。它是在 SEO 基础上的升维
在 AI 作为新信息中介的时代,可见性的规则已经改变。不是创造好内容就够了,而是要让机器能理解、信任并引用你。
继承基础配置
配置不需要重复书写。继承机制让每个层次只表达自己的差异。
Purikura的页面系统
通过五层分层继承复用架构,实现零代码修改的页面生成系统。从类型定义到页面渲染,每一层专注单一职责,实现真正的数据驱动开发。
Google Fonts 官方集成
Next.js 提供了 next/font 模块,让字体加载变得简单且性能优化。Google Fonts 是最直接的商用免费字体选择。
让错误浮现
Next.js 构建悬挂问题的根源不在工具,而在掩盖。严格类型检查不是负担,而是质量的守护者。
next-intl localePrefix:默认语言不显示前缀
理解 next-intl 中 localePrefix 配置的设计哲学,以及为什么默认语言不应在 URL 中显现。
next-intl 的服务端与客户端协同机制
理解 next-intl 如何在 Next.js App Router 中协调服务端渲染和客户端交互,以及为什么需要显式设置 locale。
减少 Next.js 启动时的工作量
开发服务器启动缓慢不是偶然。它在做的事太多了。