元数据
Next.js 提供了一个元数据API,可用于定义应用程序的元数据(例如HTML head
元素中的meta
和link
标签),以改善SEO和网络共享性。
您可以通过以下两种方式向应用程序添加元数据:
- 基于配置的元数据:在
layout.js
或page.js
文件中导出一个 静态metadata
对象 或一个动态的generateMetadata
函数。 - 基于文件的元数据:向路由段添加静态或动态生成的特殊文件。
使用这两种选项,Next.js 将自动为您的页面生成相关的 <head>
元素。您还可以使用 ImageResponse
构造函数创建动态的OG图片。
静态元数据
要定义静态元数据,请从一个 layout.js
或静态 page.js
文件中导出一个 Metadata
对象。
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: '...',
description: '...',
}
export default function Page() {}
export const metadata = {
title: '...',
description: '...',
}
export default function Page() {}
有关所有可用选项的详细信息,请参见 API参考。
动态元数据
您可以使用 generateMetadata
函数来 fetch
需要动态值的元数据。
import type { Metadata, ResolvingMetadata } from 'next'
type Props = {
params: { id: string }
searchParams: { [key: string]: string | string[] | undefined }
}
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// 读取路由参数
const id = params.id
// 获取数据
const product = await fetch(`https://.../${id}`).then((res) => res.json())
// 可选地访问并扩展(而不是替换)父级元数据
const previousImages = (await parent).openGraph?.images || []
return {
title: product.title,
openGraph: {
images: ['/some-specific-page-image.jpg', ...previousImages],
},
}
}
export default function Page({ params, searchParams }: Props) {}
export async function generateMetadata({ params, searchParams }, parent) {
// 读取路由参数
const id = params.id
// 获取数据
const product = await fetch(`https://.../${id}`).then((res) => res.json())
// 可选地访问并扩展(而不是替换)父级元数据
const previousImages = (await parent).openGraph?.images || []
return {
title: product.title,
openGraph: {
images: ['/some-specific-page-image.jpg', ...previousImages],
},
}
}
export default function Page({ params, searchParams }) {}
有关所有可用参数的信息,请参见 API 参考。
须知:
基于文件的元数据
以下是可用于元数据的特殊文件:
您可以使用这些文件进行静态元数据,或者您可以使用代码以编程方式生成这些文件。
有关实现和示例,请参见 元数据文件 API 参考和 动态图像生成。
行为
基于文件的元数据具有更高的优先级,将覆盖任何基于配置的元数据。
默认字段
即使路由没有定义元数据,也会始终添加两个默认的 meta
标签:
- meta charset 标签 设置网站的字符编码。
- meta viewport 标签 设置网站的视口宽度和缩放比例,以适应不同的设备。
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
须知:你可以覆盖默认的
viewport
meta 标签。
排序
元数据按照从根段到最终 page.js
段最近的段的顺序进行评估。例如:
app/layout.tsx
(根布局)app/blog/layout.tsx
(嵌套的博客布局)app/blog/[slug]/page.tsx
(博客页面)
合并
按照评估顺序,同一路由中多个段导出的元数据对象会浅层合并在一起,形成路由的最终元数据输出。重复的键会根据它们的顺序替换。
这意味着在早期段中定义的具有嵌套字段的元数据,如 openGraph
和 robots
,将被最后一个定义它们的段覆盖。
覆盖字段
export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acme is a...',
},
}
export const metadata = {
title: 'Blog',
openGraph: {
title: 'Blog',
},
}
// 输出:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />
在上面的例子中:
app/layout.js
中的title
被app/blog/page.js
中的title
替换。app/layout.js
中的所有openGraph
字段在app/blog/page.js
中被 替换,因为app/blog/page.js
设置了openGraph
元数据。注意缺少openGraph.description
。
如果你想在覆盖其他字段的同时在段之间共享一些嵌套字段,你可以将它们提取到一个单独的变量中:
export const openGraphImage = { images: ['http://...'] }
import { openGraphImage } from './shared-metadata'
export const metadata = {
openGraph: {
...openGraphImage,
title: 'Home',
},
}
import { openGraphImage } from '../shared-metadata'
export const metadata = {
openGraph: {
...openGraphImage,
title: 'About',
},
}
在上面的例子中,OG 图片在 app/layout.js
和 app/about/page.js
之间共享,而标题则不同。
继承字段
export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acme is a...',
},
}
export const metadata = {
title: 'About',
}
// 输出:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />
注意
app/layout.js
中的title
被app/about/page.js
中的title
替换。app/layout.js
中的所有openGraph
字段在app/about/page.js
中被继承,因为app/about/page.js
没有设置openGraph
元数据。
动态图像生成
ImageResponse
构造函数允许您使用 JSX 和 CSS 生成动态图像。这对于创建社交媒体图像(如 Open Graph 图片、Twitter 卡片等)非常有用。
要使用它,您可以从 next/og
导入 ImageResponse
:
import { ImageResponse } from 'next/og'
export async function GET() {
return new ImageResponse(
(
<div
style={{
fontSize: 128,
background: 'white',
width: '100%',
height: '100%',
display: 'flex',
textAlign: 'center',
alignItems: 'center',
justifyContent: 'center',
}}
>
Hello world!
</div>
),
{
width: 1200,
height: 600,
}
)
}
ImageResponse
与 Next.js 的其他 API 集成得很好,包括 路由处理器 和基于文件的元数据。例如,您可以在 opengraph-image.tsx
文件中使用 ImageResponse
在构建时或请求时动态生成 Open Graph 图像。
ImageResponse
支持常见的 CSS 属性,包括 flexbox 和绝对定位、自定义字体、文本换行、居中和嵌套图像。查看支持的 CSS 属性完整列表。
须知:
- 示例可在 Vercel OG Playground 中找到。
ImageResponse
使用 @vercel/og、Satori 和 Resvg 将 HTML 和 CSS 转换为 PNG。- 仅支持 Edge 运行时。默认的 Node.js 运行时将无法工作。
- 仅支持 flexbox 和 CSS 属性的子集。高级布局(例如
display: grid
)将无法工作。- 最大捆绑包大小为
500KB
。捆绑包大小包括您的 JSX、CSS、字体、图像和任何其他资产。如果您超出限制,请考虑减少任何资产的大小或在运行时获取。- 仅支持
ttf
、otf
和woff
字体格式。为了最大化字体解析速度,优先选择ttf
或otf
而不是woff
。
JSON-LD
JSON-LD 是一种结构化数据格式,搜索引擎可以用它来理解您的内容。例如,您可以用它来描述一个人、一个事件、一个组织、一部电影、一本书、一个食谱以及许多其他类型的实体。
我们当前对 JSON-LD 的建议是在您的 layout.js
或 page.js
组件中将结构化数据渲染为 <script>
标签。例如:
export default async function Page({ params }) {
const product = await getProduct(params.id)
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
image: product.image,
description: product.description,
}
return (
<section>
{/* 在页面中添加 JSON-LD */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{/* ... */}
</section>
)
}
export default async function Page({ params }) {
const product = await getProduct(params.id)
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
image: product.image,
description: product.description,
}
return (
<section>
{/* 在页面中添加 JSON-LD */}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{/* ... */}
</section>
)
}
您可以使用 Google 的 Rich Results Test 或通用的 Schema Markup Validator 来验证和测试您的结构化数据。
您可以使用社区包如 schema-dts
使用 TypeScript 输入您的 JSON-LD:
import { Product, WithContext } from 'schema-dts'
const jsonLd: WithContext<Product> = {
'@context': 'https://schema.org',
'@type': 'Product',
name: 'Next.js Sticker',
image: 'https://nextjs.org/imgs/sticker.png',
description: 'Dynamic at the speed of static.',
}