元数据
Next.js 提供了一个 Metadata API,
可用于定义应用程序元数据(例如 HTML head
元素内的 meta
和 link 标签),
以提高 SEO 和 Web 共享性能 。
有两种方法可以向应用程序添加元数据:
- 基于配置的元数据: 在
layout.js
或page.js
文件中导出静态元数据对象或动态的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() {}
有关所有可用选项,请参见 API 参考。
动态元数据
您可以使用 generateMetadata
函数获取需要动态值的元数据。
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) {}
有关所有可用参数,请参见 API 参考。
- 静态和通过
generateMetadata
动态获取的元数据仅在 Server Components 中受支持。 - 对于相同数据,
generateMetadata
、generateStaticParams
、Layouts
、Pages
和 Server Components 中的fetch
请求会自动进行记忆。如果无法使用fetch
,则可以使用 React 缓存。 - Next.js 将等待
generateMetadata
中的数据获取完成,然后再将 UI 流式传输到客户端。这确保了流式响应的第一部分包含<head>
标签。
基于文件的元数据
这些特殊文件用于元数据:
favicon.ico
、apple-icon.jpg
和icon.jpg
opengraph-image.jpg
和twitter-image.jpg
robots.txt
sitemap.xml
您可以将这些用于静态元数据,或者您可以使用代码以编程方式生成这些文件。
有关实现和示例,请参见 Metadata Files API 参考 和 Dynamic Image Generation。
行为
基于文件的元数据具有更高的优先级,并将覆盖任何基于配置的元数据。
默认字段
即使路由没有定义元数据,仍会始终添加两个默认的 meta 标签:
<meta charset>
标签设置网站的字符编码。<meta name="viewport" content="width=device-width, initial-scale=1">
标签设置网站的视口宽度和缩放,以适应不同设备。
<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
(博客页面)
合并
根据评估顺序,从同一路由中导出的多个段的 Metadata 对象将被浅合并在一起, 形成路由的最终元数据输出。 基于其顺序替换重复的键。
这意味着在较早的段中定义的包含嵌套字段(例如 openGraph
和 robots
)的元数据在最后一个定义它们的段中被覆盖。
字段覆盖
export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acme is a...',
},
}
export const metadata = {
title: 'Blog',
openGraph: {
title: 'Blog',
},
}
// Output:
// <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...',
},
}
// app/about/page.js
export const metadata = {
title: 'About',
}
// Output:
// <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
元数据。
动态图像生成
使用 JSX 和 CSS,ImageResponse
构造函数允许您生成动态图像。
这对于创建社交媒体图像(如 Open Graph 图像、Twitter 卡等)非常有用。
ImageResponse
使用 Edge Runtime,
Next.js 会自动将正确的头部添加到缓存图像的边缘,有助于提高性能并减少重新计算。
要使用它,您可以从 next/og
导入 ImageResponse
:
import { ImageResponse } from 'next/og'
export const runtime = 'edge'
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 集成良好,
包括 Route Handlers
和基于文件的 Metadata。
例如,您可以在 opengraph-image.tsx
文件中使用 ImageResponse
,
在构建时或在请求时动态生成 Open Graph 图像。
ImageResponse
支持常见的 CSS 属性,包括 flexbox
和绝对定位,自定义字体,文本换行,居中和嵌套图像。
请参阅支持的 CSS 属性的完整列表。
- 在 Vercel OG Playground 中提供了示例。
- ImageResponse 使用 @vercel/og、 Satori 和 Resvg 将 HTML 和 CSS 转换为 PNG。
- 仅支持 Edge Runtime。默认的 Node.js 运行时将不起作用。
- 仅支持
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>
)
}
您可以使用像 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.',
}