跳到内容
API 参考函数generateMetadata

generateMetadata

此页面涵盖了使用 generateMetadata 和静态元数据对象的所有基于配置的元数据选项。

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
// either Static metadata
export const metadata: Metadata = {
  title: '...',
}
 
// or Dynamic metadata
export async function generateMetadata({ params }) {
  return {
    title: '...',
  }
}

须知:

  • metadata 对象和 generateMetadata 函数导出仅在服务器组件中受支持
  • 你不能从同一路由段导出 metadata 对象和 generateMetadata 函数。

metadata 对象

要定义静态元数据,从 layout.jspage.js 文件导出一个 Metadata 对象

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: '...',
  description: '...',
}
 
export default function Page() {}

请参阅 元数据字段 以获取受支持选项的完整列表。

generateMetadata 函数

动态元数据取决于动态信息,例如当前路由参数、外部数据或父段中的 metadata,可以通过导出一个返回 Metadata 对象generateMetadata 函数来设置。

app/products/[id]/page.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> {
  // 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) {}

参数

generateMetadata 函数接受以下参数

  • props - 一个包含当前路由参数的对象

    • params - 一个包含从根段到调用 generateMetadata 的段的动态路由参数对象的对象。示例

      路由URLparams
      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 的 搜索参数的对象。示例

      URLsearchParams
      /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 来定义。
  • 对于跨 generateMetadatagenerateStaticParams、布局、页面和服务器组件的相同数据,fetch 请求会自动记忆化。如果 fetch 不可用,可以使用 React cache
  • searchParams 仅在 page.js 段中可用。
  • Next.js 的 redirect()notFound() 方法也可以在 generateMetadata 内部使用。

元数据字段

title

title 属性用于设置文档的标题。它可以定义为简单的字符串或可选的模板对象

字符串

layout.js | page.js
export const metadata = {
  title: 'Next.js',
}
<head> 输出
<title>Next.js</title>

模板对象

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    template: '...',
    default: '...',
    absolute: '...',
  },
}
默认值

title.default 可用于为未定义 title 的子路由段提供后备标题

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    default: 'Acme',
  },
}
app/about/page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {}
 
// Output: <title>Acme</title>
模板

title.template 可用于向路由段中定义的 titles 添加前缀或后缀。

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    template: '%s | Acme',
    default: 'Acme', // a default is required when creating a template
  },
}
app/about/page.tsx
import type { Metadata } from 'next'
 
export const metadata: 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 没有效果,因为页面始终是终止段(它没有任何子路由段)。
  • 如果路由未定义 titletitle.default,则 title.template无效

绝对

title.absolute 可用于提供一个忽略父段中设置的 title.template 的标题。

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    template: '%s | Acme',
  },
}
app/about/page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: {
    absolute: 'About',
  },
}
 
// Output: <title>About</title>

须知:

  • layout.js

    • title(字符串)和 title.default 定义子段(未定义自己的 title)的默认标题。如果存在,它将增强来自最近父段的 title.template
    • title.absolute 定义子段的默认标题。它忽略来自父段的 title.template
    • title.template 为子段定义了一个新的标题模板。
  • page.js

    • 如果页面未定义自己的标题,则将使用最近父级解析的标题。
    • title(字符串)定义路由的标题。如果存在,它将增强来自最近父段的 title.template
    • title.absolute 定义路由标题。它忽略来自父段的 title.template
    • title.templatepage.js 中无效,因为页面始终是路由的终止段。

description

layout.js | page.js
export const metadata = {
  description: 'The React Framework for the Web',
}
<head> 输出
<meta name="description" content="The React Framework for the Web" />

基本字段

layout.js | page.js
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.net.cn' }],
  creator: 'Jiachi Liu',
  publisher: 'Sebastian Markbåge',
  formatDetection: {
    email: false,
    address: false,
    telephone: false,
  },
}
<head> 输出
<meta name="application-name" content="Next.js" />
<meta name="author" content="Seb" />
<link rel="author" href="https://nextjs.net.cn" />
<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" />

metadataBase

metadataBase 是一个便捷选项,用于为需要完全限定 URL 的 metadata 字段设置基本 URL 前缀。

  • metadataBase 允许在当前路由段及以下定义的基于 URL 的 metadata 字段使用相对路径,而不是原本需要的绝对 URL。
  • 字段的相对路径将与 metadataBase 组合以形成完全限定的 URL。
  • 如果未配置,metadataBase自动填充默认值
layout.js | page.js
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',
  },
}
<head> 输出
<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

默认值

如果未配置,metadataBase 具有默认值

在 Vercel 上

  • 对于生产部署,将使用 VERCEL_PROJECT_PRODUCTION_URL
  • 对于预览部署,VERCEL_BRANCH_URL 将优先,如果不存在,则回退到 VERCEL_URL

如果这些值存在,它们将用作 metadataBase默认值,否则回退到 https://127.0.0.1:${process.env.PORT || 3000}。这允许 Open Graph 图像在本地构建以及 Vercel 预览和生产部署上工作。当覆盖默认值时,我们建议使用环境变量来计算 URL。这允许为本地开发、暂存和生产环境配置 URL。

有关这些环境变量的更多详细信息,请参阅 系统环境变量 文档。

URL 组成

URL 组成优先考虑开发者意图,而不是默认目录遍历语义。

  • metadataBasemetadata 字段之间的尾部斜杠被规范化。
  • metadata 字段中的“绝对”路径(通常会替换整个 URL 路径)被视为“相对”路径(从 metadataBase 的末尾开始)。

例如,给定以下 metadataBase

app/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  metadataBase: new URL('https://acme.com'),
}

任何继承上述 metadataBase 并设置其自身值的 metadata 字段都将按如下方式解析

metadata 字段已解析 URL
/https://acme.com
./https://acme.com
paymentshttps://acme.com/payments
/paymentshttps://acme.com/payments
./paymentshttps://acme.com/payments
../paymentshttps://acme.com/payments
https://beta.acme.com/paymentshttps://beta.acme.com/payments

openGraph

layout.js | page.js
export const metadata = {
  openGraph: {
    title: 'Next.js',
    description: 'The React Framework for the Web',
    url: 'https://nextjs.net.cn',
    siteName: 'Next.js',
    images: [
      {
        url: 'https://nextjs.net.cn/og.png', // Must be an absolute URL
        width: 800,
        height: 600,
      },
      {
        url: 'https://nextjs.net.cn/og-alt.png', // Must be an absolute URL
        width: 1800,
        height: 1600,
        alt: 'My custom alt',
      },
    ],
    videos: [
      {
        url: 'https://nextjs.net.cn/video.mp4', // Must be an absolute URL
        width: 800,
        height: 600,
      },
    ],
    audio: [
      {
        url: 'https://nextjs.net.cn/audio.mp3', // Must be an absolute URL
      },
    ],
    locale: 'en_US',
    type: 'website',
  },
}
<head> 输出
<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.net.cn/" />
<meta property="og:site_name" content="Next.js" />
<meta property="og:locale" content="en_US" />
<meta property="og:image" content="https://nextjs.net.cn/og.png" />
<meta property="og:image:width" content="800" />
<meta property="og:image:height" content="600" />
<meta property="og:image" content="https://nextjs.net.cn/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.net.cn/video.mp4" />
<meta property="og:video:width" content="800" />
<meta property="og:video:height" content="600" />
<meta property="og:audio" content="https://nextjs.net.cn/audio.mp3" />
<meta property="og:type" content="website" />
layout.js | page.js
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'],
  },
}
<head> 输出
<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 将自动为你生成正确的元数据,而无需将配置导出与实际文件同步。

robots

layout.tsx | page.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,
    },
  },
}
<head> 输出
<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 将自动为你生成正确的元数据,而无需将配置导出与实际文件同步。

layout.js | page.js
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',
    },
  },
}
<head> 输出
<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"
/>
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',
      },
    ],
  },
}
<head> 输出
<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-* 元标签在 Microsoft Edge 的 Chromium 构建版本中不再受支持,因此不再需要。

themeColor

已弃用metadata 中的 themeColor 选项已在 Next.js 14 中弃用。请改用 viewport 配置

colorScheme

已弃用metadata 中的 colorScheme 选项已在 Next.js 14 中弃用。请改用 viewport 配置

manifest

Web 应用程序清单,如 Web Application Manifest 规范 中所定义。

layout.js | page.js
export const metadata = {
  manifest: 'https://nextjs.net.cn/manifest.json',
}
<head> 输出
<link rel="manifest" href="https://nextjs.net.cn/manifest.json" />

twitter

Twitter 规范(令人惊讶地)不仅用于 X(以前称为 Twitter)。

了解更多关于 Twitter Card 标记参考

layout.js | page.js
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.net.cn/og.png'], // Must be an absolute URL
  },
}
<head> 输出
<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.net.cn/og.png" />
layout.js | page.js
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.net.cn/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',
      },
    },
  },
}
<head> 输出
<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.net.cn/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 配置

verification

layout.js | page.js
export const metadata = {
  verification: {
    google: 'google',
    yandex: 'yandex',
    yahoo: 'yahoo',
    other: {
      me: ['my-email', 'my-link'],
    },
  },
}
<head> 输出
<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" />

appleWebApp

layout.js | page.js
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)',
      },
    ],
  },
}
<head> 输出
<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"
/>

alternates

layout.js | page.js
export const metadata = {
  alternates: {
    canonical: 'https://nextjs.net.cn',
    languages: {
      'en-US': 'https://nextjs.net.cn/en-US',
      'de-DE': 'https://nextjs.net.cn/de-DE',
    },
    media: {
      'only screen and (max-width: 600px)': 'https://nextjs.net.cn/mobile',
    },
    types: {
      'application/rss+xml': 'https://nextjs.net.cn/rss',
    },
  },
}
<head> 输出
<link rel="canonical" href="https://nextjs.net.cn" />
<link rel="alternate" hreflang="en-US" href="https://nextjs.net.cn/en-US" />
<link rel="alternate" hreflang="de-DE" href="https://nextjs.net.cn/de-DE" />
<link
  rel="alternate"
  media="only screen and (max-width: 600px)"
  href="https://nextjs.net.cn/mobile"
/>
<link
  rel="alternate"
  type="application/rss+xml"
  href="https://nextjs.net.cn/rss"
/>
layout.js | page.js
export const metadata = {
  appLinks: {
    ios: {
      url: 'https://nextjs.net.cn/ios',
      app_store_id: 'app_store_id',
    },
    android: {
      package: 'com.example.android/package',
      app_name: 'app_name_android',
    },
    web: {
      url: 'https://nextjs.net.cn/web',
      should_fallback: true,
    },
  },
}
<head> 输出
<meta property="al:ios:url" content="https://nextjs.net.cn/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.net.cn/web" />
<meta property="al:web:should_fallback" content="true" />

archives

描述历史意义的记录、文档或其他材料的集合 (来源)。

layout.js | page.js
export const metadata = {
  archives: ['https://nextjs.net.cn/13'],
}
<head> 输出
<link rel="archives" href="https://nextjs.net.cn/13" />

assets

layout.js | page.js
export const metadata = {
  assets: ['https://nextjs.net.cn/assets'],
}
<head> 输出
<link rel="assets" href="https://nextjs.net.cn/assets" />

bookmarks

layout.js | page.js
export const metadata = {
  bookmarks: ['https://nextjs.net.cn/13'],
}
<head> 输出
<link rel="bookmarks" href="https://nextjs.net.cn/13" />

category

layout.js | page.js
export const metadata = {
  category: 'technology',
}
<head> 输出
<meta name="category" content="technology" />

facebook

您可以将 Facebook 应用程序或 Facebook 帐户连接到您的网页,以便用于某些 Facebook 社交插件 Facebook 文档

须知:您可以指定 appId 或 admins,但不能同时指定两者。

layout.js | page.js
export const metadata = {
  facebook: {
    appId: '12345678',
  },
}
<head> 输出
<meta property="fb:app_id" content="12345678" />
layout.js | page.js
export const metadata = {
  facebook: {
    admins: '12345678',
  },
}
<head> 输出
<meta property="fb:admins" content="12345678" />

如果您想生成多个 fb:admins meta 标签,可以使用数组值。

layout.js | page.js
export const metadata = {
  facebook: {
    admins: ['12345678', '87654321'],
  },
}
<head> 输出
<meta property="fb:admins" content="12345678" />
<meta property="fb:admins" content="87654321" />

other

所有元数据选项都应使用内置支持进行涵盖。但是,可能存在特定于您的网站或品牌的自定义元数据标签,或者刚刚发布的新元数据标签。您可以使用 other 选项来渲染任何自定义元数据标签。

layout.js | page.js
export const metadata = {
  other: {
    custom: 'meta',
  },
}
<head> 输出
<meta name="custom" content="meta" />

如果您想生成多个具有相同键的 meta 标签,可以使用数组值。

layout.js | page.js
export const metadata = {
  other: {
    custom: ['meta1', 'meta2'],
  },
}
<head> 输出
<meta name="custom" content="meta1" /> <meta name="custom" content="meta2" />

不支持的元数据

以下元数据类型目前没有内置支持。但是,它们仍然可以在布局或页面本身中渲染。

元数据建议
<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 关键字,可用于提示浏览器可能需要外部资源。浏览器使用此信息根据关键字应用预加载优化。

虽然 Metadata API 不直接支持这些提示,但您可以使用新的 ReactDOM 方法 将它们安全地插入到文档的 <head> 中。

app/preload-resources.tsx
'use client'
 
import ReactDOM from 'react-dom'
 
export function PreloadResources() {
  ReactDOM.preload('...', { as: '...' })
  ReactDOM.preconnect('...', { crossOrigin: '...' })
  ReactDOM.prefetchDNS('...')
 
  return '...'
}

在页面渲染(浏览器)生命周期的早期开始加载资源。MDN 文档

ReactDOM.preload(href: string, options: { as: string })
<head> 输出
<link rel="preload" href="..." as="..." />

抢先启动与来源的连接。MDN 文档

ReactDOM.preconnect(href: string, options?: { crossOrigin?: string })
<head> 输出
<link rel="preconnect" href="..." crossorigin />

尝试在请求资源之前解析域名。MDN 文档

ReactDOM.prefetchDNS(href: string)
<head> 输出
<link rel="dns-prefetch" href="..." />

须知:

  • 这些方法目前仅在客户端组件中受支持,客户端组件在初始页面加载时仍为服务器端渲染。
  • Next.js 内置功能(如 next/fontnext/imagenext/script)会自动处理相关的资源提示。

类型

您可以使用 Metadata 类型为您的元数据添加类型安全性。如果您在 IDE 中使用 内置的 TypeScript 插件,则无需手动添加类型,但如果您愿意,仍然可以显式添加它。

metadata 对象

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'Next.js',
}

generateMetadata 函数

常规函数

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
export function generateMetadata(): Metadata {
  return {
    title: 'Next.js',
  }
}

异步函数

layout.tsx | page.tsx
import type { Metadata } from 'next'
 
export async function generateMetadata(): Promise<Metadata> {
  return {
    title: 'Next.js',
  }
}

使用分段 props

layout.tsx | page.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) {}

使用父级元数据

layout.tsx | page.tsx
import type { Metadata, ResolvingMetadata } from 'next'
 
export async function generateMetadata(
  { params, searchParams }: Props,
  parent: ResolvingMetadata
): Promise<Metadata> {
  return {
    title: 'Next.js',
  }
}

JavaScript 项目

对于 JavaScript 项目,您可以使用 JSDoc 添加类型安全性。

layout.js | page.js
/** @type {import("next").Metadata} */
export const metadata = {
  title: 'Next.js',
}

流式元数据

从 v15.2 开始,generateMetadata 返回的元数据将流式传输到客户端。这允许 Next.js 在元数据解析后立即将其注入到 HTML 中。

由于页面元数据通常主要针对机器人和爬虫,因此 Next.js 将继续阻止渲染,直到 HTML 受限机器人 的元数据解析完成。

某些机器人(如 Googlebot)可以执行 JavaScript 并能够检查完整的页面 DOM,这意味着它们需要阻塞元数据。但是,像 Twitterbot 这样的机器人无法在爬取页面时执行 JavaScript,它们属于 HTML 受限 类别。

Next.js 会自动检测传入请求的用户代理,以确定是提供流式元数据还是回退到阻塞元数据。

如果您需要自定义此列表,您可以使用 next.config.js 中的 htmlLimitedBots 选项手动定义它们。Next.js 将确保当请求您的网页时,与此正则表达式匹配的用户代理收到阻塞元数据。指定 htmlLimitedBots 配置将覆盖 Next.js 的默认列表,使您可以完全控制哪些用户代理应选择此行为。这是高级行为,默认设置对于大多数情况应该足够了。

next.config.js
module.exports = {
  htmlLimitedBots: 'MySpecialBot|MyAnotherSpecialBot|SimpleCrawler',
}

注意: Next.js 包括 HTML 受限机器人的默认列表

版本历史

版本变更
v15.2.0generateMetadata 引入了流式传输支持。
v13.2.0viewportthemeColorcolorScheme 已弃用,转而使用 viewport 配置
v13.2.0引入了 metadatagenerateMetadata