您可以使用 metadata 对象或 generateMetadata 函数来定义元数据。
metadata 对象要定义静态元数据,请从 layout.js 或 page.js 文件中导出一个 Metadata object。
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "...",
description: "...",
};
export default function Page() {}export const metadata = {
title: "...",
description: "...",
};
export default function Page() {}查看 Metadata Fields 以获取支持选项的完整列表。
generateMetadata 函数动态元数据取决于动态信息,例如当前路由参数、外部数据或父级段中的 metadata,可以通过导出一个返回 Metadata object 的 generateMetadata 函数来设置。
解析 generateMetadata 是页面渲染的一部分。如果页面可以预渲染并且 generateMetadata 不引入动态行为,则生成的元数据会包含在页面的初始 HTML 中。
否则,从 generateMetadata 解析出的元数据在发送初始 UI 后可以进行流式传输。
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;
// 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;
// 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 }) {}为了 params 和 searchParams 的类型补全,您可以分别为页面和布局将第一个参数类型化为 PageProps<'/route'> 或 LayoutProps<'/route'>。
须知:
- 元数据可以添加到
layout.js和page.js文件中。- Next.js 将自动解析元数据,并为页面创建相关的
<head>标签。metadata对象和generateMetadata函数的导出仅在 Server Components 中支持。- 您不能在同一个路由段中同时导出
metadata对象和generateMetadata函数。generateMetadata内部的fetch请求会自动记忆化,以便在generateMetadata、generateStaticParams、布局、页面和服务器组件之间共享相同数据。- 如果
fetch不可用,可以使用 React 的cache函数。- 基于文件的元数据具有更高的优先级,并将覆盖
metadata对象和generateMetadata函数。
generateMetadata 函数接受以下参数:
props - 一个包含当前路由参数的对象:
params - 一个包含 动态路由参数 的对象,范围从根段到调用 generateMetadata 的段。例如:
| Route | 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 object。
须知:
- 如果元数据不依赖于运行时信息,则应使用静态
metadataobject 而不是generateMetadata来定义。fetch请求会自动记忆化,以便在generateMetadata、generateStaticParams、布局、页面和服务器组件之间共享相同数据。如果fetch不可用,可以使用 React 的cache函数。searchParams仅在page.js段中可用。- Next.js 的
redirect()和notFound()方法也可以在generateMetadata中使用。
支持以下字段:
titletitle 属性用于设置文档的标题。它可以定义为一个简单的 字符串 或一个可选的 模板对象。
export const metadata = {
title: "Next.js",
};<title>Next.js</title>defaulttitle.default 可用于为未定义 title 的子路由段提供一个备用标题。
import type { Metadata } from "next";
export const metadata: Metadata = {
title: {
default: "Acme",
},
};import type { Metadata } from "next";
export const metadata: Metadata = {};
// Output: <title>Acme</title>templatetitle.template 可用于为在子路由段中定义的 titles 添加前缀或后缀。
import type { Metadata } from "next";
export const metadata: Metadata = {
title: {
template: "%s | Acme",
default: "Acme", // a default is required when creating a template
},
};export const metadata = {
title: {
template: "%s | Acme",
default: "Acme", // a default is required when creating a template
},
};import type { Metadata } from "next";
export const metadata: Metadata = {
title: "About",
};
// Output: <title>About | Acme</title>export const metadata = {
title: "About",
};
// Output: <title>About | Acme</title>须知:
title.template应用于子路由段,而不是它所定义的路由段。这意味着:
- 当您添加
title.template时,title.default是必需的。- 在
layout.js中定义的title.template不会应用于同一路由段中page.js里定义的title。- 在
page.js中定义的title.template没有效果,因为页面始终是终止段(它没有任何子路由段)。- 如果路由没有定义
title或title.default,title.template会没有效果。
absolutetitle.absolute 可用于提供一个忽略在父级段中设置的 title.template 的标题。
import type { Metadata } from "next";
export const metadata: Metadata = {
title: {
template: "%s | Acme",
},
};export const metadata = {
title: {
template: "%s | Acme",
},
};import type { Metadata } from "next";
export const metadata: Metadata = {
title: {
absolute: "About",
},
};
// Output: <title>About</title>export const metadata = {
title: {
absolute: 'About',
},
}
// Output: <title>About</title>
```**须知**:
- `layout.js`
- `title` (string) 和 `title.default` 定义了子段的默认标题(未定义自身 `title` 的子段)。如果最近的父段存在 `title.template`,它将增强该模板。
- `title.absolute` 定义了子段的默认标题。它忽略父段的 `title.template`。
- `title.template` 为子段定义了一个新的标题模板。
- `page.js`
- 如果页面未定义自身标题,则将使用最近父级的解析标题。
- `title` (string) 定义了路由标题。如果最近的父段存在 `title.template`,它将增强该模板。
- `title.absolute` 定义了路由标题。它忽略父段的 `title.template`。
- `title.template` 在 `page.js` 中无效,因为页面始终是路由的终止段。
### `description`
```jsx filename="layout.js | page.js"
export const metadata = {
description: 'The React Framework for the Web',
}<meta name="description" content="The React Framework for the Web" />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,
},
};<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="referrer" content="origin-when-cross-origin" />
<meta name="color-scheme" content="dark" />
<meta name="creator" content="Jiachi Liu" />
<meta name="publisher" content="Sebastian Markbåge" />
<meta name="format-detection" content="telephone=no, address=no, email=no" />metadataBasemetadataBase 是一个便捷选项,用于为需要完全限定 URL 的 metadata 字段设置基础 URL 前缀。
metadataBase 允许在当前路由段及以下定义的基于 URL 的 metadata 字段使用相对路径,而不是其他情况下所需的绝对 URL。metadataBase 组合,形成一个完全限定 URL。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",
},
};<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" />须知:
metadataBase通常设置在根app/layout.js中,以应用于所有路由中基于 URL 的metadata字段。- 所有需要绝对 URL 的基于 URL 的
metadata字段都可以通过metadataBase选项进行配置。metadataBase可以包含子域名(例如https://app.acme.com)或基础路径(例如https://acme.com/start/from/here)。- 如果
metadata字段提供了绝对 URL,metadataBase将被忽略。- 在未配置
metadataBase的情况下,在基于 URL 的metadata字段中使用相对路径将导致构建错误。- Next.js 会将
metadataBase(例如https://acme.com/)与相对字段(例如/path)之间的重复斜杠标准化为单个斜杠(例如https://acme.com/path)。
URL 构成优先考虑开发者意图而非默认的目录遍历语义。
metadataBase 和 metadata 字段之间的尾部斜杠会被标准化。metadata 字段中的“绝对”路径(通常会替换整个 URL 路径)被视为“相对”路径(从 metadataBase 的末尾开始)。例如,给定以下 metadataBase:
import type { Metadata } from "next";
export const metadata: Metadata = {
metadataBase: new URL("https://acme.com"),
};export const metadata = {
metadataBase: new URL("https://acme.com"),
};任何继承上述 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 |
openGraphexport 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", // Must be an absolute URL
width: 800,
height: 600,
},
{
url: "https://nextjs.org/og-alt.png", // Must be an absolute URL
width: 1800,
height: 1600,
alt: "My custom alt",
},
],
videos: [
{
url: "https://nextjs.org/video.mp4", // Must be an absolute URL
width: 800,
height: 600,
},
],
audio: [
{
url: "https://nextjs.org/audio.mp3", // Must be an absolute URL
},
],
locale: "en_US",
type: "website",
},
};<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" />export const metadata = {
openGraph: {
title: "Next.js",
description: "The React Framework for the Web",
type: "article",
publishedTime: "2023-01-01T00:00:00.000Z",
authors: ["Seb", "Josh"],
},
};<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="2023-01-01T00:00:00.000Z" />
<meta property="article:author" content="Seb" />
<meta property="article:author" content="Josh" />须知:
- 对 Open Graph 图像使用基于文件的元数据 API 可能会更方便。这样,你无需将配置导出与实际文件同步,基于文件的 API 会自动为你生成正确的元数据。
robotsimport 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,
},
},
};<meta name="robots" content="index, follow" />
<meta
name="googlebot"
content="index, follow, max-video-preview:-1, max-image-preview:large, max-snippet:-1"
/>icons须知: 我们建议尽可能对图标使用基于文件的元数据 API。这样,你无需将配置导出与实际文件同步,基于文件的 API 会自动为你生成正确的元数据。
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",
},
},
};<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"
/>
``````jsx filename="layout.js | page.js" 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', }, ],
}, }<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"
/>值得注意的是:
msapplication-*meta 标签在 Chromium 构建的 Microsoft Edge 中已不再受支持,因此也不再需要。
themeColor已废弃:
metadata中的themeColor选项自 Next.js 14 起已废弃。请改用viewport配置。
colorScheme已废弃:
metadata中的colorScheme选项自 Next.js 14 起已废弃。请改用viewport配置。
manifest一个 Web 应用程序清单,其定义见 Web 应用程序清单规范。
export const metadata = {
manifest: "https://nextjs.org/manifest.json",
};<link rel="manifest" href="https://nextjs.org/manifest.json" />twitterTwitter 规范(令人惊讶地)不仅仅用于 X(前身为 Twitter)。
了解更多关于 Twitter 卡片标记参考 的信息。
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"], // Must be an absolute URL
},
};<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" />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",
},
},
},
};<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" />viewport已废弃:
metadata中的viewport选项自 Next.js 14 起已废弃。请改用viewport配置。
verificationexport const metadata = {
verification: {
google: "google",
yandex: "yandex",
yahoo: "yahoo",
other: {
me: ["my-email", "my-link"],
},
},
};<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" />appleWebAppexport 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)",
},
],
},
};<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
href="/assets/startup/apple-touch-startup-image-1536x2008.png"
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"
/>alternatesexport 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",
},
},
};<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"
/>appLinksexport 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,
},
},
};<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" />archives描述具有历史意义的记录、文档或其他材料的集合(来源)。
export const metadata = {
archives: ["https://nextjs.org/13"],
};<link rel="archives" href="https://nextjs.org/13" />assetsexport const metadata = {
assets: ["https://nextjs.org/assets"],
};<link rel="assets" href="https://nextjs.org/assets" />bookmarksexport const metadata = {
bookmarks: ["https://nextjs.org/13"],
};<link rel="bookmarks" href="https://nextjs.org/13" />categoryexport const metadata = {
category: "technology",
};<meta name="category" content="technology" />facebook您可以将 Facebook 应用程序或 Facebook 帐户连接到您的网页,以使用某些 Facebook 社交插件 Facebook 文档。
值得注意的是: 您可以指定
appId或admins,但不能同时指定两者。
export const metadata = {
facebook: {
appId: "12345678",
},
};<meta property="fb:app_id" content="12345678" /> ``````jsx filename="layout.js |
page.js" export const metadata = { facebook: { admins: '12345678', }, }<meta property="fb:admins" content="12345678" />如果您想生成多个 fb:admins meta 标签,可以使用数组值。
export const metadata = {
facebook: {
admins: ["12345678", "87654321"],
},
};<meta property="fb:admins" content="12345678" />
<meta property="fb:admins" content="87654321" />pinterest您可以在网页上启用或禁用 Pinterest Rich Pins。
export const metadata = {
pinterest: {
richPin: true,
},
};<meta name="pinterest-rich-pin" content="true" />other所有元数据选项都应通过内置支持来覆盖。然而,您的站点可能有一些特定的自定义元数据标签,或者刚发布的新元数据标签。您可以使用 other 选项来渲染任何自定义元数据标签。
export const metadata = {
other: {
custom: "meta",
},
};<meta name="custom" content="meta" />如果您想生成多个相同键的 meta 标签,可以使用数组值。
export const metadata = {
other: {
custom: ["meta1", "meta2"],
},
};<meta name="custom" content="meta1" /> <meta name="custom" content="meta2" />您可以通过使用 Metadata 类型为您的元数据添加类型安全。如果您在 IDE 中使用了内置的 TypeScript 插件,则无需手动添加该类型,但如果需要,您仍然可以显式添加它。
metadata objectimport type { Metadata } from "next";
export const metadata: Metadata = {
title: "Next.js",
};generateMetadata functionimport type { Metadata } from "next";
export function generateMetadata(): Metadata {
return {
title: "Next.js",
};
}import type { Metadata } from "next";
export async function generateMetadata(): Promise<Metadata> {
return {
title: "Next.js",
};
}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) {}import type { Metadata, ResolvingMetadata } from "next";
export async function generateMetadata(
{ params, searchParams }: Props,
parent: ResolvingMetadata
): Promise<Metadata> {
return {
title: "Next.js",
};
}对于 JavaScript 项目,您可以使用 JSDoc 添加类型安全。
/** @type {import("next").Metadata} */
export const metadata = {
title: "Next.js",
};以下元数据类型目前没有内置支持。但是,它们仍然可以在布局或页面本身中渲染。
| 元数据 | 建议 |
|---|---|
<meta http-equiv="..."> | 通过 redirect()、Proxy、Security Headers 使用相应的 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 关键字,可用于向浏览器提示可能需要外部资源。浏览器使用此信息根据关键字应用预加载优化。
虽然 Metadata API 不直接支持这些提示,但您可以使用新的 ReactDOM 方法 将它们安全地插入到文档的 <head> 中。
"use client";
import ReactDOM from "react-dom";
export function PreloadResources() {
ReactDOM.preload("...", { as: "..." });
ReactDOM.preconnect("...", { crossOrigin: "..." });
ReactDOM.prefetchDNS("...");
return "...";
}"use client";
import ReactDOM from "react-dom";
export function PreloadResources() {
ReactDOM.preload("...", { as: "..." });
ReactDOM.preconnect("...", { crossOrigin: "..." });
ReactDOM.prefetchDNS("...");
return "...";
}<link rel="preload">在页面渲染(浏览器)生命周期的早期开始加载资源。 MDN 文档。
ReactDOM.preload(href: string, options: { as: string })<link rel="preload" href="..." as="..." /><link rel="preconnect">抢先发起与源的连接。 MDN 文档。
ReactDOM.preconnect(href: string, options?: { crossOrigin?: string })<link rel="preconnect" href="..." crossorigin /><link rel="dns-prefetch">在请求资源之前尝试解析域名。 MDN 文档。
ReactDOM.prefetchDNS(href: string)<link rel="dns-prefetch" href="..." />须知:
- 这些方法目前仅在客户端组件中受支持,这些组件在初始页面加载时仍然进行服务器端渲染。
- Next.js 的内置功能,例如
next/font、next/image和next/script,会自动处理相关的资源提示。
即使路由未定义元数据,也会始终添加以下两个默认 meta 标签:
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />须知:您可以覆盖默认的
viewportmeta 标签。
流式元数据允许 Next.js 渲染并将初始 UI 发送到浏览器,而无需等待 generateMetadata 完成。当 generateMetadata 解析完成后,生成的元数据标签会附加到 <body> 标签。我们已验证,元数据能被执行 JavaScript 并检查完整 DOM 的机器人(bot)正确解析(例如 Googlebot)。
对于无法执行 JavaScript 的仅限 HTML 的机器人(例如 facebookexternalhit),元数据仍会阻塞页面渲染。生成的元数据将在 <head> 标签中提供。
Next.js 会通过检查 User Agent 头部自动检测仅限 HTML 的机器人。你可以在 Next.js 配置文件中使用 htmlLimitedBots 选项来覆盖默认的 User Agent 列表。
要完全禁用流式元数据:
import type { NextConfig } from "next";
const config: NextConfig = {
htmlLimitedBots: /.*/,
};
export default config;module.exports = {
htmlLimitedBots: /.*/,
};流式元数据通过减少 TTFB 来提高感知性能,并有助于降低 LCP 时间。
覆盖 htmlLimitedBots 可能会导致更长的响应时间。流式元数据是一项高级功能,默认设置在大多数情况下应该足够。
元数据按顺序评估,从根 Segment 开始,一直到最接近最终 page.js Segment 的部分。例如:
app/layout.tsx (根布局)app/blog/layout.tsx (嵌套博客布局)app/blog/[slug]/page.tsx (博客页面)遵循 评估顺序,从同一路由中的多个 Segment 导出的元数据对象会被浅层合并,以形成路由的最终元数据输出。重复的键会根据其顺序被替换。
这意味着在较早的 Segment 中定义的带有嵌套字段的元数据(例如 openGraph 和 robots)会被最后一个定义它们的 Segment 覆盖。
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 的缺失。如果你想在不同 Segment 之间共享一些嵌套字段,同时覆盖其他字段,你可以将它们提取到一个单独的变量中:
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",
};
// 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 元数据。| 版本 | 更改 |
|---|---|
v15.2.0 | 为 generateMetadata 引入了流式支持。 |
v13.2.0 | viewport、themeColor 和 colorScheme 已弃用,取而代之的是 viewport 配置。 |
v13.2.0 | 引入了 metadata 和 generateMetadata。 |