元数据
Next.js 有一个元数据 API,可用于定义应用元数据(例如 HTML head
元素内的 meta
和 link
标签),以改进 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:
-
基于配置的元数据:将 静态
metadata
对象 或动态generateMetadata
功能 导出到layout.js
或page.js
文件中。¥Config-based Metadata: Export a static
metadata
object or a dynamicgenerateMetadata
function in alayout.js
orpage.js
file. -
基于文件的元数据:将静态或动态生成的特殊文件添加到路由段。
¥File-based Metadata: Add static or dynamically generated special files to route segments.
通过这两个选项,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.对于跨
generateMetadata
、generateStaticParams
、布局、页面和服务器组件的相同数据,fetch
请求自动为 memoized。如果fetch
不可用,则响应 可以使用cache
。¥
fetch
requests are automatically memoized for the same data acrossgenerateMetadata
,generateStaticParams
, Layouts, Pages, and Server Components. Reactcache
can be used iffetch
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:
-
元字符集标签 设置网站的字符编码。
¥The meta charset tag sets the character encoding for the website.
-
元视口标签 设置网站的视口宽度和比例,以适应不同的设备。
¥The meta viewport tag sets the viewport width and scale for the website to adjust for different devices.
<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:
-
app/layout.tsx
(根布局)¥
app/layout.tsx
(Root Layout) -
app/blog/layout.tsx
(嵌套博客布局)¥
app/blog/layout.tsx
(Nested Blog Layout) -
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.
这意味着在较早段中定义的具有嵌套字段(例如 openGraph
和 robots
)的元数据将被最后一个段覆盖以定义它们。
¥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
中的title
被app/blog/page.js
中的title
替换。¥
title
fromapp/layout.js
is replaced bytitle
inapp/blog/page.js
. -
app/layout.js
中的所有openGraph
字段都在app/blog/page.js
中替换,因为app/blog/page.js
设置了openGraph
元数据。请注意缺少openGraph.description
。¥All
openGraph
fields fromapp/layout.js
are replaced inapp/blog/page.js
becauseapp/blog/page.js
setsopenGraph
metadata. Note the absence ofopenGraph.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.js
和 app/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
中的title
被app/about/page.js
中的title
替换。¥
title
fromapp/layout.js
is replaced bytitle
inapp/about/page.js
. -
app/layout.js
中的所有openGraph
字段都会在app/about/page.js
中继承,因为app/about/page.js
未设置openGraph
元数据。¥All
openGraph
fields fromapp/layout.js
are inherited inapp/about/page.js
becauseapp/about/page.js
doesn't setopenGraph
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/og、Satori 和 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.仅支持
ttf
、otf
和woff
字体格式。为了最大化字体解析速度,ttf
或otf
优于woff
。¥Only
ttf
,otf
, andwoff
font formats are supported. To maximize the font parsing speed,ttf
orotf
are preferred overwoff
.
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.js
或 page.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.',
}