NextJS中文文档 - Generate Metadata
你可以使用 metadata
对象或 generateMetadata
函数来定义元数据。
metadata
对象
要定义静态元数据,从 layout.js
或 page.js
文件中导出一个 Metadata
对象。
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: '...',
description: '...',
}
export default function Page() {}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
jsx
export const metadata = {
title: '...',
description: '...',
}
export default function Page() {}
1
2
3
4
5
6
2
3
4
5
6
查看元数据字段获取支持选项的完整列表。
generateMetadata
函数
依赖于动态信息的元数据,如当前路由参数、外部数据或父段中的 metadata
,可以通过导出返回 Metadata
对象的 generateMetadata
函数来设置。
tsx
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> {
// 读取路由参数
const { id } = await params
// 获取数据
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) {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
jsx
export async function generateMetadata({ params, searchParams }, parent) {
// 读取路由参数
const { id } = await params
// 获取数据
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 }) {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
须知:
- 元数据可以添加到
layout.js
和page.js
文件中。- Next.js 将自动解析元数据,并为页面创建相关的
<head>
标签。metadata
对象和generateMetadata
函数导出仅在服务器组件中支持。- 你不能从同一路由段同时导出
metadata
对象和generateMetadata
函数。- 在
generateMetadata
内部的fetch
请求会自动在generateMetadata
、generateStaticParams
、布局、页面和服务器组件之间进行记忆化。- 如果
fetch
不可用,可以使用 Reactcache
函数。- 基于文件的元数据具有更高的优先级,并会覆盖
metadata
对象和generateMetadata
函数。
参考
参数
generateMetadata
函数接受以下参数:
props
- 包含当前路由参数的对象:params
- 包含从根段到调用generateMetadata
的段的动态路由参数对象。示例:路由 URL params
app/shop/[slug]/page.js
/shop/1
{ slug: '1' }
app/shop/[tag]/[item]/page.js
/shop/1/2
{ tag: '1', item: '2' }
app/shop/[...slug]/page.js
/shop/1/2
{ slug: ['1', '2'] }
searchParams
- 包含当前 URL 的搜索参数的对象。示例:URL searchParams
/shop?a=1
{ a: '1' }
/shop?a=1&b=2
{ a: '1', b: '2' }
/shop?a=1&a=2
{ a: ['1', '2'] }
parent
- 来自父路由段的已解析元数据的 Promise。
返回值
generateMetadata
应该返回一个包含一个或多个元数据字段的 Metadata
对象。
须知:
- 如果元数据不依赖于运行时信息,应该使用静态的
metadata
对象 而不是generateMetadata
来定义。fetch
请求在generateMetadata
、generateStaticParams
、布局、页面和服务器组件之间自动进行记忆化。如果fetch
不可用,可以使用 Reactcache
函数。searchParams
只在page.js
段中可用。- Next.js 的
redirect()
和notFound()
方法也可以在generateMetadata
中使用。
元数据字段
支持以下字段:
title
title
属性用于设置文档的标题。它可以定义为简单的字符串或可选的模板对象。
字符串
jsx
export const metadata = {
title: 'Next.js',
}
1
2
3
2
3
html
<title>Next.js</title>
1
default
title.default
可用于为没有定义 title
的子路由段提供回退标题。
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: {
default: 'Acme',
},
}
1
2
3
4
5
6
7
2
3
4
5
6
7
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {}
// 输出: <title>Acme</title>
1
2
3
4
5
2
3
4
5
template
title.template
可用于为子路由段中定义的 titles
添加前缀或后缀。
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: {
template: '%s | Acme',
default: 'Acme', // 创建模板时需要默认值
},
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
jsx
export const metadata = {
title: {
template: '%s | Acme',
default: 'Acme', // 创建模板时需要默认值
},
}
1
2
3
4
5
6
2
3
4
5
6
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'About',
}
// 输出: <title>About | Acme</title>
1
2
3
4
5
6
7
2
3
4
5
6
7
jsx
export const metadata = {
title: 'About',
}
// 输出: <title>About | Acme</title>
1
2
3
4
5
2
3
4
5
须知:
title.template
适用于子路由段,而不是定义它的段。这意味着:
- 添加
title.template
时需要title.default
。- 在
layout.js
中定义的title.template
不会应用于同一路由段的page.js
中定义的title
。- 在
page.js
中定义的title.template
没有效果,因为页面始终是终止段(它没有任何子路由段)。如果路由没有定义
title
或title.default
,则title.template
没有效果。
absolute
title.absolute
可用于提供一个忽略父段中设置的 title.template
的标题。
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: {
template: '%s | Acme',
},
}
1
2
3
4
5
6
7
2
3
4
5
6
7
jsx
export const metadata = {
title: {
template: '%s | Acme',
},
}
1
2
3
4
5
2
3
4
5
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: {
absolute: 'About',
},
}
// Output: <title>About</title>
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
jsx
export const metadata = {
title: {
absolute: 'About',
},
}
// Output: <title>About</title>
1
2
3
4
5
6
7
2
3
4
5
6
7
须知:
layout.js
title
(字符串) 和title.default
为不定义自己的title
的子段定义默认标题。如果存在,它将使用最近父段的title.template
进行增强。title.absolute
为子段定义默认标题。它忽略父段中的title.template
。title.template
为子段定义一个新的标题模板。
page.js
- 如果页面未定义自己的标题,将使用最近父级解析的标题。
title
(字符串) 定义路由的标题。如果存在,它将使用最近父段的title.template
进行增强。title.absolute
定义路由标题。它忽略父段中的title.template
。title.template
在page.js
中没有效果,因为页面始终是路由的终止段。
description
jsx
export const metadata = {
description: 'The React Framework for the Web',
}
1
2
3
2
3
html
<meta name="description" content="The React Framework for the Web" />
1
Other fields
基本字段
通过添加标准元数据字段可以改进 SEO、社交媒体分享以及 Web 体验:
jsx
export const metadata = {
generator: 'Next.js',
applicationName: 'Next.js',
referrer: 'origin-when-cross-origin',
keywords: ['Next.js', 'React', 'JavaScript'],
authors: [{ name: 'Seb' }, { name: 'Josh', url: 'https://nextjs.org' }],
creator: 'Jiachi Liu',
publisher: 'Sebastian Markbåge',
formatDetection: {
email: false,
address: false,
telephone: false,
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
html
<meta name="application-name" content="Next.js" />
<meta name="author" content="Seb" />
<link rel="author" href="https://nextjs.org" />
<meta name="author" content="Josh" />
<meta name="generator" content="Next.js" />
<meta name="keywords" content="Next.js,React,JavaScript" />
<meta name="creator" content="Jiachi Liu" />
<meta name="publisher" content="Sebastian Markbåge" />
<meta name="format-detection" content="telephone=no, address=no, email=no" />
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
metadataBase
metadataBase
是一种便捷选项,用于为需要完全限定 URL 的 metadata
字段设置基本 URL 前缀。
metadataBase
允许当前路由段及其以下定义的基于 URL 的metadata
字段使用相对路径,而不是原本需要的绝对 URL。- 字段的相对路径将与
metadataBase
组合,形成完全限定的 URL。
jsx
export const metadata = {
metadataBase: new URL('https://acme.com'),
alternates: {
canonical: '/',
languages: {
'en-US': '/en-US',
'de-DE': '/de-DE',
},
},
openGraph: {
images: '/og-image.png',
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
html
<link rel="canonical" href="https://acme.com" />
<link rel="alternate" hreflang="en-US" href="https://acme.com/en-US" />
<link rel="alternate" hreflang="de-DE" href="https://acme.com/de-DE" />
<meta property="og:image" content="https://acme.com/og-image.png" />
1
2
3
4
2
3
4
须知:
metadataBase
通常在根app/layout.js
中设置,以应用于所有路由的基于 URL 的metadata
字段。- 所有需要绝对 URL 的基于 URL 的
metadata
字段都可以用metadataBase
选项配置。metadataBase
可以包含子域名,如https://app.acme.com
或基本路径,如https://acme.com/start/from/here
。- 如果
metadata
字段提供了绝对 URL,则metadataBase
将被忽略。- 在基于 URL 的
metadata
字段中使用相对路径而不配置metadataBase
将导致构建错误。- Next.js 将规范化
metadataBase
(例如https://acme.com/
)和相对字段(例如/path
)之间的重复斜杠为单个斜杠(例如https://acme.com/path
)。
URL 组合
URL 组合倾向于开发者的意图,而不是默认的目录遍历语义。
metadataBase
和metadata
字段之间的尾部斜杠会被规范化。metadata
字段中的"绝对"路径(通常会替换整个 URL 路径)被视为"相对"路径(从metadataBase
的末尾开始)。
例如,给定以下 metadataBase
:
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
metadataBase: new URL('https://acme.com'),
}
1
2
3
4
5
2
3
4
5
jsx
export const metadata = {
metadataBase: new URL('https://acme.com'),
}
1
2
3
2
3
继承上述 metadataBase
并设置自己的值的任何 metadata
字段将按以下方式解析:
metadata 字段 | 解析后的 URL |
---|---|
/ | https://acme.com |
./ | https://acme.com |
payments | https://acme.com/payments |
/payments | https://acme.com/payments |
./payments | https://acme.com/payments |
../payments | https://acme.com/payments |
https://beta.acme.com/payments | https://beta.acme.com/payments |
openGraph
jsx
export const metadata = {
openGraph: {
title: 'Next.js',
description: 'The React Framework for the Web',
url: 'https://nextjs.org',
siteName: 'Next.js',
images: [
{
url: 'https://nextjs.org/og.png', // 必须是绝对 URL
width: 800,
height: 600,
},
{
url: 'https://nextjs.org/og-alt.png', // 必须是绝对 URL
width: 1800,
height: 1600,
alt: 'My custom alt',
},
],
videos: [
{
url: 'https://nextjs.org/video.mp4', // 必须是绝对 URL
width: 800,
height: 600,
},
],
audio: [
{
url: 'https://nextjs.org/audio.mp3', // 必须是绝对 URL
},
],
locale: 'en_US',
type: 'website',
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
html
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="The React Framework for the Web" />
<meta property="og:url" content="https://nextjs.org/" />
<meta property="og:site_name" content="Next.js" />
<meta property="og:locale" content="en_US" />
<meta property="og:image" content="https://nextjs.org/og.png" />
<meta property="og:image:width" content="800" />
<meta property="og:image:height" content="600" />
<meta property="og:image" content="https://nextjs.org/og-alt.png" />
<meta property="og:image:width" content="1800" />
<meta property="og:image:height" content="1600" />
<meta property="og:image:alt" content="My custom alt" />
<meta property="og:video" content="https://nextjs.org/video.mp4" />
<meta property="og:video:width" content="800" />
<meta property="og:video:height" content="600" />
<meta property="og:audio" content="https://nextjs.org/audio.mp3" />
<meta property="og:type" content="website" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
jsx
export const metadata = {
openGraph: {
title: 'Next.js',
description: 'The React Framework for the Web',
type: 'article',
publishedTime: '01T00:00:00.000Z',
authors: ['Seb', 'Josh'],
},
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
html
<meta property="og:title" content="Next.js" />
<meta property="og:description" content="The React Framework for the Web" />
<meta property="og:type" content="article" />
<meta property="article:published_time" content="01T00:00:00.000Z" />
<meta property="article:author" content="Seb" />
<meta property="article:author" content="Josh" />
1
2
3
4
5
6
2
3
4
5
6
须知: 我们建议使用基于文件的元数据 API 处理 Open Graph 图像。与必须同步配置导出和实际文件相比,基于文件的 API 将自动为您生成正确的元数据。
robots
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
robots: {
index: true,
follow: true,
nocache: false,
googleBot: {
index: true,
follow: true,
noimageindex: false,
'max-video-preview': -1,
'max-image-preview': 'large',
'max-snippet': -1,
},
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
html
<meta name="robots" content="index, follow" />
<meta
name="googlebot"
content="index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1"
/>
1
2
3
4
5
2
3
4
5
icons
须知: 我们建议尽可能使用基于文件的元数据 API处理图标。与必须同步配置导出和实际文件相比,基于文件的 API 将自动为您生成正确的元数据。
jsx
export const metadata = {
icons: {
icon: '/icon.png',
shortcut: '/shortcut-icon.png',
apple: '/apple-icon.png',
other: {
rel: 'apple-touch-icon-precomposed',
url: '/apple-touch-icon-precomposed.png',
},
},
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
html
<link rel="shortcut icon" href="/shortcut-icon.png" />
<link rel="icon" href="/icon.png" />
<link rel="apple-touch-icon" href="/apple-icon.png" />
<link rel="apple-touch-icon-precomposed" href="/apple-touch-icon-precomposed.png" />
1
2
3
4
2
3
4
jsx
export const metadata = {
icons: {
icon: [
{ url: '/icon.png' },
new URL('/icon.png', 'https://example.com'),
{ url: '/icon-dark.png', media: '(prefers-color-scheme: dark)' },
],
shortcut: ['/shortcut-icon.png'],
apple: [
{ url: '/apple-icon.png' },
{ url: '/apple-icon-x3.png', sizes: '180x180', type: 'image/png' },
],
other: [
{
rel: 'apple-touch-icon-precomposed',
url: '/apple-touch-icon-precomposed.png',
},
],
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
html
<link rel="shortcut icon" href="/shortcut-icon.png" />
<link rel="icon" href="/icon.png" />
<link rel="icon" href="https://example.com/icon.png" />
<link rel="icon" href="/icon-dark.png" media="(prefers-color-scheme: dark)" />
<link rel="apple-touch-icon" href="/apple-icon.png" />
<link rel="apple-touch-icon-precomposed" href="/apple-touch-icon-precomposed.png" />
<link rel="apple-touch-icon" href="/apple-icon-x3.png" sizes="180x180" type="image/png" />
1
2
3
4
5
6
7
2
3
4
5
6
7
须知: 微软 Edge 的 Chromium 版本不再支持
msapplication-*
元标签,因此不再需要这些标签。
themeColor
已弃用: 自 Next.js 14 起,
metadata
中的themeColor
选项已被弃用。请改用viewport
配置。
colorScheme
已弃用: 自 Next.js 14 起,
metadata
中的colorScheme
选项已被弃用。请改用viewport
配置。
manifest
网络应用程序清单,在网络应用程序清单规范中定义。
jsx
export const metadata = {
manifest: 'https://nextjs.org/manifest.json',
}
1
2
3
2
3
html
<link rel="manifest" href="https://nextjs.org/manifest.json" />
1
twitter
Twitter 规范(令人惊讶地)不仅用于 X(原 Twitter)。
了解更多关于 Twitter 卡片标记参考。
jsx
export const metadata = {
twitter: {
card: 'summary_large_image',
title: 'Next.js',
description: 'The React Framework for the Web',
siteId: '1467726470533754880',
creator: '@nextjs',
creatorId: '1467726470533754880',
images: ['https://nextjs.org/og.png'], // 必须是绝对 URL
},
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
html
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site:id" content="1467726470533754880" />
<meta name="twitter:creator" content="@nextjs" />
<meta name="twitter:creator:id" content="1467726470533754880" />
<meta name="twitter:title" content="Next.js" />
<meta name="twitter:description" content="The React Framework for the Web" />
<meta name="twitter:image" content="https://nextjs.org/og.png" />
1
2
3
4
5
6
7
2
3
4
5
6
7
jsx
export const metadata = {
twitter: {
card: 'app',
title: 'Next.js',
description: 'The React Framework for the Web',
siteId: '1467726470533754880',
creator: '@nextjs',
creatorId: '1467726470533754880',
images: {
url: 'https://nextjs.org/og.png',
alt: 'Next.js Logo',
},
app: {
name: 'twitter_app',
id: {
iphone: 'twitter_app://iphone',
ipad: 'twitter_app://ipad',
googleplay: 'twitter_app://googleplay',
},
url: {
iphone: 'https://iphone_url',
ipad: 'https://ipad_url',
},
},
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
html
<meta name="twitter:site:id" content="1467726470533754880" />
<meta name="twitter:creator" content="@nextjs" />
<meta name="twitter:creator:id" content="1467726470533754880" />
<meta name="twitter:title" content="Next.js" />
<meta name="twitter:description" content="The React Framework for the Web" />
<meta name="twitter:card" content="app" />
<meta name="twitter:image" content="https://nextjs.org/og.png" />
<meta name="twitter:image:alt" content="Next.js Logo" />
<meta name="twitter:app:name:iphone" content="twitter_app" />
<meta name="twitter:app:id:iphone" content="twitter_app://iphone" />
<meta name="twitter:app:id:ipad" content="twitter_app://ipad" />
<meta name="twitter:app:id:googleplay" content="twitter_app://googleplay" />
<meta name="twitter:app:url:iphone" content="https://iphone_url" />
<meta name="twitter:app:url:ipad" content="https://ipad_url" />
<meta name="twitter:app:name:ipad" content="twitter_app" />
<meta name="twitter:app:name:googleplay" content="twitter_app" />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
viewport
已弃用: 自 Next.js 14 起,
metadata
中的viewport
选项已被弃用。请改用viewport
配置。
verification
jsx
export const metadata = {
verification: {
google: 'google',
yandex: 'yandex',
yahoo: 'yahoo',
other: {
me: ['my-email', 'my-link'],
},
},
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
html
<meta name="google-site-verification" content="google" />
<meta name="y_key" content="yahoo" />
<meta name="yandex-verification" content="yandex" />
<meta name="me" content="my-email" />
<meta name="me" content="my-link" />
1
2
3
4
5
2
3
4
5
appleWebApp
jsx
export const metadata = {
itunes: {
appId: 'myAppStoreID',
appArgument: 'myAppArgument',
},
appleWebApp: {
title: 'Apple Web App',
statusBarStyle: 'black-translucent',
startupImage: [
'/assets/startup/apple-touch-startup-image-768x1004.png',
{
url: '/assets/startup/apple-touch-startup-image-1536x2008.png',
media: '(device-width: 768px) and (device-height: 1024px)',
},
],
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
html
<meta name="apple-itunes-app" content="app-id=myAppStoreID, app-argument=myAppArgument" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-title" content="Apple Web App" />
<link
href="/assets/startup/apple-touch-startup-image-768x1004.png"
rel="apple-touch-startup-image"
/>
<link media="(device-width: 768px) and (device-height: 1024px)" rel="apple-touch-startup-image" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
alternates
jsx
export const metadata = {
alternates: {
canonical: 'https://nextjs.org',
languages: {
'en-US': 'https://nextjs.org/en-US',
'de-DE': 'https://nextjs.org/de-DE',
},
media: {
'only screen and (max-width: 600px)': 'https://nextjs.org/mobile',
},
types: {
'application/rss+xml': 'https://nextjs.org/rss',
},
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
html
<link rel="canonical" href="https://nextjs.org" />
<link rel="alternate" hreflang="en-US" href="https://nextjs.org/en-US" />
<link rel="alternate" hreflang="de-DE" href="https://nextjs.org/de-DE" />
<link rel="alternate" media="only screen and (max-width: 600px)" href="https://nextjs.org/mobile" />
<link rel="alternate" type="application/rss+xml" href="https://nextjs.org/rss" />
1
2
3
4
5
2
3
4
5
appLinks
jsx
export const metadata = {
appLinks: {
ios: {
url: 'https://nextjs.org/ios',
app_store_id: 'app_store_id',
},
android: {
package: 'com.example.android/package',
app_name: 'app_name_android',
},
web: {
url: 'https://nextjs.org/web',
should_fallback: true,
},
},
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
html
<meta property="al:ios:url" content="https://nextjs.org/ios" />
<meta property="al:ios:app_store_id" content="app_store_id" />
<meta property="al:android:package" content="com.example.android/package" />
<meta property="al:android:app_name" content="app_name_android" />
<meta property="al:web:url" content="https://nextjs.org/web" />
<meta property="al:web:should_fallback" content="true" />
1
2
3
4
5
6
2
3
4
5
6
archives
描述历史记录、文档或其他具有历史意义的材料的集合(来源)。
jsx
export const metadata = {
archives: ['https://nextjs.org/13'],
}
1
2
3
2
3
html
<link rel="archives" href="https://nextjs.org/13" />
1
assets
jsx
export const metadata = {
assets: ['https://nextjs.org/assets'],
}
1
2
3
2
3
html
<link rel="assets" href="https://nextjs.org/assets" />
1
bookmarks
jsx
export const metadata = {
bookmarks: ['https://nextjs.org/13'],
}
1
2
3
2
3
html
<link rel="bookmarks" href="https://nextjs.org/13" />
1
category
jsx
export const metadata = {
category: 'technology',
}
1
2
3
2
3
html
<meta name="category" content="technology" />
1
facebook
您可以将 Facebook 应用或 Facebook 账户连接到您的网页,以便使用某些 Facebook 社交插件 Facebook 文档
须知: 您只能指定 appId 或 admins 之一,不能同时指定两者。
jsx
export const metadata = {
facebook: {
appId: '12345678',
},
}
1
2
3
4
5
2
3
4
5
html
<meta property="fb:app_id" content="12345678" />
1
jsx
export const metadata = {
facebook: {
admins: '12345678',
},
}
1
2
3
4
5
2
3
4
5
html
<meta property="fb:admins" content="12345678" />
1
如果您想生成多个 fb:admins 元标签,可以使用数组值。
jsx
export const metadata = {
facebook: {
admins: ['12345678', '87654321'],
},
}
1
2
3
4
5
2
3
4
5
html
<meta property="fb:admins" content="12345678" />
<meta property="fb:admins" content="87654321" />
1
2
2
pinterest
您可以在网页上启用或禁用 Pinterest 富媒体引脚。
jsx
export const metadata = {
pinterest: {
richPin: true,
},
}
1
2
3
4
5
2
3
4
5
html
<meta name="pinterest-rich-pin" content="true" />
1
other
所有元数据选项都应使用内置支持进行覆盖。但是,可能存在特定于您网站的自定义元数据标签,或者刚刚发布的全新元数据标签。您可以使用 other
选项来渲染任何自定义元数据标签。
jsx
export const metadata = {
other: {
custom: 'meta',
},
}
1
2
3
4
5
2
3
4
5
html
<meta name="custom" content="meta" />
1
如果您想生成多个相同键的元标签,可以使用数组值。
jsx
export const metadata = {
other: {
custom: ['meta1', 'meta2'],
},
}
1
2
3
4
5
2
3
4
5
html
<meta name="custom" content="meta1" />
<meta name="custom" content="meta2" />
1
2
2
不支持的元数据
以下元数据类型目前没有内置支持。但是,它们仍然可以在布局或页面本身中渲染。
类型
您可以使用 Metadata
类型为元数据添加类型安全。如果您在 IDE 中使用内置 TypeScript 插件,则不需要手动添加类型,但您仍然可以显式添加它。
metadata
对象
tsx
import type { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Next.js',
}
1
2
3
4
5
2
3
4
5
generateMetadata
函数
常规函数
tsx
import type { Metadata } from 'next'
export function generateMetadata(): Metadata {
return {
title: 'Next.js',
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
异步函数
tsx
import type { Metadata } from 'next'
export async function generateMetadata(): Promise<Metadata> {
return {
title: 'Next.js',
}
}
1
2
3
4
5
6
7
2
3
4
5
6
7
使用段参数
tsx
import type { Metadata } from 'next'
type Props = {
params: Promise<{ id: string }>
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
}
export function generateMetadata({ params, searchParams }: Props): Metadata {
return {
title: 'Next.js',
}
}
export default function Page({ params, searchParams }: Props) {}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
使用父元数据
tsx
import type { Metadata, ResolvingMetadata } from 'next'
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata,
): Promise<Metadata> {
return {
title: 'Next.js',
}
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
JavaScript 项目
对于 JavaScript 项目,您可以使用 JSDoc 添加类型安全。
js
/** @type {import("next").Metadata} */
export const metadata = {
title: 'Next.js',
}
1
2
3
4
2
3
4
元数据 | 建议 |
---|---|
<meta http-equiv="..."> | 通过 redirect() 、中间件、安全头 使用适当的 HTTP 头 |
<base> | 在布局或页面本身中渲染标签。 |
<noscript> | 在布局或页面本身中渲染标签。 |
<style> | 了解有关 Next.js 中的样式的更多信息。 |
<script> | 了解有关使用脚本的更多信息。 |
<link rel="stylesheet" /> | 直接在布局或页面本身中 import 样式表。 |
<link rel="preload /> | 使用 ReactDOM preload 方法 |
<link rel="preconnect" /> | 使用 ReactDOM preconnect 方法 |
<link rel="dns-prefetch" /> | 使用 ReactDOM prefetchDNS 方法 |
资源提示
<link>
元素有许多 rel
关键字,可用于提示浏览器可能需要外部资源。浏览器使用此信息根据关键字应用预加载优化。
虽然元数据 API 不直接支持这些提示,但您可以使用新的 ReactDOM
方法 安全地将它们插入到文档的 <head>
中。
tsx
'use client'
import ReactDOM from 'react-dom'
export function PreloadResources() {
ReactDOM.preload('...', { as: '...' })
ReactDOM.preconnect('...', { crossOrigin: '...' })
ReactDOM.prefetchDNS('...')
return '...'
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
jsx
'use client'
import ReactDOM from 'react-dom'
export function PreloadResources() {
ReactDOM.preload('...', { as: '...' })
ReactDOM.preconnect('...', { crossOrigin: '...' })
ReactDOM.prefetchDNS('...')
return '...'
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
<link rel="preload">
在页面渲染(浏览器)生命周期早期开始加载资源。MDN 文档。
tsx
ReactDOM.preload(href: string, options: { as: string })
1
html
<link rel="preload" href="..." as="..." />
1
<link rel="preconnect">
预先建立与源的连接。MDN 文档。
tsx
ReactDOM.preconnect(href: string, options?: { crossOrigin?: string })
1
html
<link rel="preconnect" href="..." crossorigin />
1
<link rel="dns-prefetch">
尝试在请求资源之前解析域名。MDN 文档。
tsx
ReactDOM.prefetchDNS(href: string)
1
html
<link rel="dns-prefetch" href="..." />
1
须知:
- 这些方法目前仅在客户端组件中支持,这些组件在初始页面加载时仍然进行服务器端渲染。
- Next.js 内置功能如
next/font
、next/image
和next/script
自动处理相关资源提示。
行为
默认字段
即使路由没有定义元数据,也会始终添加两个默认 meta
标签:
- meta charset 标签 设置网站的字符编码。
- meta viewport 标签 设置网站的视口宽度和缩放,以适应不同的设备。
html
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
1
2
2
须知: 您可以覆盖默认的
viewport
meta 标签。
流式元数据
由 generateMetadata
返回的元数据会流式传输到客户端。这允许 Next.js 在元数据解析后立即将其注入到 HTML 中。
由于页面元数据主要针对机器人和爬虫,Next.js 将为能够执行 JavaScript 并检查完整页面 DOM 的机器人(例如 Googlebot
)流式传输元数据。但是,元数据将继续阻塞HTML 受限机器人(例如 Twitterbot
)的页面渲染,因为这些机器人在爬取时无法执行 JavaScript。
Next.js 自动检测传入请求的用户代理,以确定是提供流式元数据还是回退到阻塞元数据。
如果您需要自定义此列表,可以在 next.config.js
中使用 htmlLimitedBots
选项手动定义它们。Next.js 将确保与此正则表达式匹配的用户代理在请求您的网页时接收阻塞元数据。
ts
import type { NextConfig } from 'next'
const config: NextConfig = {
htmlLimitedBots: 'MySpecialBot|MyAnotherSpecialBot|SimpleCrawler',
}
export default config
1
2
3
4
5
6
7
2
3
4
5
6
7
js
module.exports = {
htmlLimitedBots: 'MySpecialBot|MyAnotherSpecialBot|SimpleCrawler',
}
1
2
3
2
3
指定 htmlLimitedBots
配置将覆盖 Next.js 的默认列表,让您可以完全控制哪些用户代理应该选择使用此行为。这是高级行为,默认值对于大多数情况应该足够了。
排序
元数据按顺序评估,从根段开始,然后向下到最终 page.js
段最近的段。例如:
app/layout.tsx
(根布局)app/blog/layout.tsx
(嵌套博客布局)app/blog/[slug]/page.tsx
(博客页面)
合并
按照评估顺序,从同一路由的多个段导出的元数据对象浅层合并在一起,形成路由的最终元数据输出。重复的键将根据它们的顺序替换。
这意味着具有嵌套字段的元数据,如在较早段中定义的 openGraph
和 robots
,将被最后定义它们的段覆盖。
覆盖字段
jsx
export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acme is a...',
},
}
1
2
3
4
5
6
7
2
3
4
5
6
7
jsx
export const metadata = {
title: 'Blog',
openGraph: {
title: 'Blog',
},
}
// Output:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
在上面的例子中:
app/layout.js
中的title
被app/blog/page.js
中的title
替换。app/layout.js
中的所有openGraph
字段都在app/blog/page.js
中被替换,因为app/blog/page.js
设置了openGraph
元数据。请注意缺少openGraph.description
。
如果您想在段之间共享一些嵌套字段而覆盖其他字段,可以将它们提取到单独的变量中:
jsx
export const openGraphImage = { images: ['http://...'] }
1
jsx
import { openGraphImage } from './shared-metadata'
export const metadata = {
openGraph: {
...openGraphImage,
title: 'Home',
},
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
jsx
import { openGraphImage } from '../shared-metadata'
export const metadata = {
openGraph: {
...openGraphImage,
title: 'About',
},
}
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
在上面的例子中,OG 图像在 app/layout.js
和 app/about/page.js
之间共享,而标题不同。
继承字段
jsx
export const metadata = {
title: 'Acme',
openGraph: {
title: 'Acme',
description: 'Acme is a...',
},
}
1
2
3
4
5
6
7
2
3
4
5
6
7
jsx
export const metadata = {
title: 'About',
}
// Output:
// <title>About</title>
// <meta property="og:title" content="Acme" />
// <meta property="og:description" content="Acme is a..." />
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
Notes
title
fromapp/layout.js
is replaced bytitle
inapp/about/page.js
.- All
openGraph
fields fromapp/layout.js
are inherited inapp/about/page.js
becauseapp/about/page.js
doesn't setopenGraph
metadata.
Version History
Version | Changes |
---|---|
v15.2.0 | Introduced streaming support to generateMetadata . |
v13.2.0 | viewport , themeColor , and colorScheme deprecated in favor of the viewport configuration. |
v13.2.0 | metadata and generateMetadata introduced. |