Skip to main content

元数据

Next.js 有一个元数据 API,可用于定义应用元数据(例如 HTML head 元素内的 metalink 标签),以改进 SEO 和 Web 可共享性。

¥Next.js has a Metadata API that can be used to define your application metadata (e.g. meta and link tags inside your HTML head element) for improved SEO and web shareability.

你可以通过两种方式将元数据添加到应用中:

¥There are two ways you can add metadata to your application:

通过这两个选项,Next.js 将为你的页面自动生成相关的 <head> 元素。你还可以使用 ImageResponse 构造函数创建动态 OG 图片。

¥With both these options, Next.js will automatically generate the relevant <head> elements for your pages. You can also create dynamic OG images using the ImageResponse constructor.

静态元数据

¥Static Metadata

要定义静态元数据,请从 layout.js 或静态 page.js 文件导出 Metadata 对象

¥To define static metadata, export a Metadata object from a layout.js or static page.js file.

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 参考

¥For all the available options, see the API Reference.

动态元数据

¥Dynamic Metadata

你可以使用 generateMetadata 函数来处理需要动态值的 fetch 元数据。

¥You can use generateMetadata function to fetch metadata that requires dynamic values.

import type { Metadata, ResolvingMetadata } from 'next'

type Props = {
params: Promise<{ id: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}

export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
// read route params
const id = (await params).id

// fetch data
const product = await fetch(`https://.../${id}`).then((res) => res.json())

// optionally access and extend (rather than replace) parent metadata
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) {
// read route params
const id = (await params).id

// fetch data
const product = await fetch(`https://.../${id}`).then((res) => res.json())

// optionally access and extend (rather than replace) parent metadata
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 参考

¥For all the available params, see the API Reference.

很高兴知道:

¥Good to know:

  • 仅服务器组件支持通过 generateMetadata 的静态和动态元数据。

    ¥Both static and dynamic metadata through generateMetadata are only supported in Server Components.

  • 对于跨 generateMetadatagenerateStaticParams、布局、页面和服务器组件的相同数据,fetch 请求自动为 memoized。如果 fetch 不可用,则响应 可以使用 cache

    ¥fetch requests are automatically memoized for the same data across generateMetadata, generateStaticParams, Layouts, Pages, and Server Components. React cache can be used if fetch is unavailable.

  • Next.js 将等待 generateMetadata 内的数据获取完成,然后再将 UI 流式传输到客户端。这保证了 流式响应 的第一部分包含 <head> 标签。

    ¥Next.js will wait for data fetching inside generateMetadata to complete before streaming UI to the client. This guarantees the first part of a streamed response includes <head> tags.

基于文件的元数据

¥File-based metadata

这些特殊文件可用于元数据:

¥These special files are available for metadata:

你可以将它们用于静态元数据,也可以使用代码以编程方式生成这些文件。

¥You can use these for static metadata, or you can programmatically generate these files with code.

有关实现和示例,请参阅 元数据文件 API 参考和 动态图片生成

¥For implementation and examples, see the Metadata Files API Reference and Dynamic Image Generation.

行为

¥Behavior

基于文件的元数据具有更高的优先级,并将覆盖任何基于配置的元数据。

¥File-based metadata has the higher priority and will override any config-based metadata.

默认字段

¥Default Fields

即使路由未定义元数据,也始终会添加两个默认的 meta 标记:

¥There are two default meta tags that are always added even if a route doesn't define metadata:

<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />

很高兴知道:你可以覆盖默认的 viewport 元标记。

¥Good to know: You can overwrite the default viewport meta tag.

排序

¥Ordering

元数据按顺序评估,从根段开始一直到最接近最终 page.js 段的段。例如:

¥Metadata is evaluated in order, starting from the root segment down to the segment closest to the final page.js segment. For example:

  1. app/layout.tsx(根布局)

    ¥app/layout.tsx (Root Layout)

  2. app/blog/layout.tsx(嵌套博客布局)

    ¥app/blog/layout.tsx (Nested Blog Layout)

  3. app/blog/[slug]/page.tsx(博客页面)

    ¥app/blog/[slug]/page.tsx (Blog Page)

合并

¥Merging

遵循 评估顺序,从同一路由中的多个段导出的元数据对象被浅层合并在一起,形成路由的最终元数据输出。重复的键将根据其顺序进行替换。

¥Following the evaluation order, Metadata objects exported from multiple segments in the same route are shallowly merged together to form the final metadata output of a route. Duplicate keys are replaced based on their ordering.

这意味着在较早段中定义的具有嵌套字段(例如 openGraphrobots)的元数据将被最后一个段覆盖以定义它们。

¥This means metadata with nested fields such as openGraph and robots that are defined in an earlier segment are overwritten by the last segment to define them.

覆盖字段

¥Overwriting fields

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" />

在上面的例子中:

¥In the example above:

  • app/layout.js 中的 titleapp/blog/page.js 中的 title 替换。

    ¥title from app/layout.js is replaced by title in app/blog/page.js.

  • app/layout.js 中的所有 openGraph 字段都在 app/blog/page.js 中替换,因为 app/blog/page.js 设置了 openGraph 元数据。请注意缺少 openGraph.description

    ¥All openGraph fields from app/layout.js are replaced in app/blog/page.js because app/blog/page.js sets openGraph metadata. Note the absence of openGraph.description.

如果你想在段之间共享一些嵌套字段并覆盖其他字段,则可以将它们拉出到一个单独的变量中:

¥If you'd like to share some nested fields between segments while overwriting others, you can pull them out into a separate variable:

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.jsapp/about/page.js 之间共享,但标题不同。

¥In the example above, the OG image is shared between app/layout.js and app/about/page.js while the titles are different.

继承字段

¥Inheriting fields

export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acme is a...',
},
}
export const metadata = {
title: 'About',
}

// Output:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />

注意

¥Notes

  • app/layout.js 中的 titleapp/about/page.js 中的 title 替换。

    ¥title from app/layout.js is replaced by title in app/about/page.js.

  • app/layout.js 中的所有 openGraph 字段都会在 app/about/page.js 中继承,因为 app/about/page.js 未设置 openGraph 元数据。

    ¥All openGraph fields from app/layout.js are inherited in app/about/page.js because app/about/page.js doesn't set openGraph metadata.

动态图片生成

¥Dynamic Image Generation

ImageResponse 构造函数允许你使用 JSX 和 CSS 生成动态图片。这对于创建社交媒体图片(例如 Open Graph 图片、Twitter 卡等)非常有用。

¥The ImageResponse constructor allows you to generate dynamic images using JSX and CSS. This is useful for creating social media images such as Open Graph images, Twitter cards, and more.

要使用它,你可以从 next/og 导入 ImageResponse

¥To use it, you can import ImageResponse from next/og:

import { ImageResponse } from 'next/og'

export async function GET() {
return new ImageResponse(
(
<div

>
Hello world!
</div>
),
{
width: 1200,
height: 600,
}
)
}

ImageResponse 与其他 Next.js API 集成良好,包括 路由处理程序 和基于文件的元数据。例如,你可以在 opengraph-image.tsx 文件中使用 ImageResponse 在构建时或在请求时动态生成 Open Graph 图片。

¥ImageResponse integrates well with other Next.js APIs, including Route Handlers and file-based Metadata. For example, you can use ImageResponse in a opengraph-image.tsx file to generate Open Graph images at build time or dynamically at request time.

ImageResponse 支持常见的 CSS 属性,包括 Flexbox 和绝对定位、自定义字体、文本换行、居中和嵌套图片。查看支持的 CSS 属性的完整列表

¥ImageResponse supports common CSS properties including flexbox and absolute positioning, custom fonts, text wrapping, centering, and nested images. See the full list of supported CSS properties.

很高兴知道:

¥Good to know:

  • Vercel OG 在线运行 中提供了示例。

    ¥Examples are available in the Vercel OG Playground.

  • ImageResponse 使用 @vercel/ogSatori 和 Resvg 将 HTML 和 CSS 转换为 PNG。

    ¥ImageResponse uses @vercel/og, Satori, and Resvg to convert HTML and CSS into PNG.

  • 仅支持 Edge 运行时。默认的 Node.js 运行时将不起作用。

    ¥Only the Edge Runtime is supported. The default Node.js runtime will not work.

  • 仅支持 flexbox 和 CSS 属性的子集。高级布局(例如 display: grid)将不起作用。

    ¥Only flexbox and a subset of CSS properties are supported. Advanced layouts (e.g. display: grid) will not work.

  • 最大打包尺寸为 500KB。打包包大小包括 JSX、CSS、字体、图片和任何其他资源。如果超出限制,请考虑减少任何资源的大小或在运行时获取。

    ¥Maximum bundle size of 500KB. The bundle size includes your JSX, CSS, fonts, images, and any other assets. If you exceed the limit, consider reducing the size of any assets or fetching at runtime.

  • 仅支持 ttfotfwoff 字体格式。为了最大化字体解析速度,ttfotf 优于 woff

    ¥Only ttf, otf, and woff font formats are supported. To maximize the font parsing speed, ttf or otf are preferred over woff.

JSON-LD

JSON-LD 是一种结构化数据格式,搜索引擎可以使用它来理解你的内容。例如,你可以使用它来描述一个人、一个事件、一个组织、一部电影、一本书、一个秘诀和许多其他类型的实体。

¥JSON-LD is a format for structured data that can be used by search engines to understand your content. For example, you can use it to describe a person, an event, an organization, a movie, a book, a recipe, and many other types of entities.

我们当前对 JSON-LD 的建议是将结构化数据渲染为 layout.jspage.js 组件中的 <script> 标签。例如:

¥Our current recommendation for JSON-LD is to render structured data as a <script> tag in your layout.js or page.js components. For example:

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>

<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>

<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>

</section>
)
}

你可以使用 丰富的结果测试 for Google 或通用 模式标记验证器 来验证和测试你的结构化数据。

¥You can validate and test your structured data with the Rich Results Test for Google or the generic Schema Markup Validator.

你可以使用 schema-dts 等社区包通过 TypeScript 键入 JSON-LD:

¥You can type your JSON-LD with TypeScript using community packages like schema-dts:

import { Product, WithContext } from 'schema-dts'

const jsonLd: WithContext<Product> = {
'@context': 'https://schema.org',
'@type': 'Product',
name: 'Next.js Sticker',
image: 'https://next.nodejs.cn/imgs/sticker.png',
description: 'Dynamic at the speed of static.',
}