跳至内容

元数据

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

您可以通过两种方式向应用程序添加元数据

  • 基于配置的元数据:在 layout.jspage.js 文件中导出一个 静态 metadata 对象 或一个动态 generateMetadata 函数
  • 基于文件的元数据:将静态或动态生成的特殊文件添加到路由段。

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

静态元数据

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

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

有关所有可用选项,请参阅 API 参考

动态元数据

您可以使用 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) {}

有关所有可用参数,请参阅 API 参考

需要了解:

  • 通过 generateMetadata 的静态和动态元数据**仅在服务器组件中受支持**。
  • 对于 generateMetadatagenerateStaticParams、布局、页面和服务器组件中的相同数据,获取 请求会自动 进行记忆化。如果 获取 不可用,则可以使用 React 缓存
  • 在将 UI 流式传输到客户端之前,Next.js 将等待 generateMetadata 内的数据获取完成。这保证了 流式响应 的第一部分包含 <head> 标签。

基于文件的元数据

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

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

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

行为

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

默认字段

即使路由没有定义元数据,也会始终添加两个默认的meta标签。

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

注意:您可以覆盖默认的viewport元标签。

顺序

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

  1. app/layout.tsx(根布局)
  2. app/blog/layout.tsx(嵌套博客布局)
  3. app/blog/[slug]/page.tsx(博客页面)

合并

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

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

覆盖字段

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
app/blog/page.js
export const metadata = {
  title: 'Blog',
  openGraph: {
    title: 'Blog',
  },
}
 
// Output:
// <title>Blog</title>
// <meta property="og:title" content="Blog" />

在上面的示例中

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

如果您希望在覆盖其他字段的同时在段之间共享一些嵌套字段,可以将它们提取到一个单独的变量中。

app/shared-metadata.js
export const openGraphImage = { images: ['http://...'] }
app/page.js
import { openGraphImage } from './shared-metadata'
 
export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'Home',
  },
}
app/about/page.js
import { openGraphImage } from '../shared-metadata'
 
export const metadata = {
  openGraph: {
    ...openGraphImage,
    title: 'About',
  },
}

在上面的示例中,OG图片在app/layout.jsapp/about/page.js之间共享,而标题不同。

继承字段

app/layout.js
export const metadata = {
  title: 'Acme',
  openGraph: {
    title: 'Acme',
    description: 'Acme is a...',
  },
}
app/about/page.js
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.jstitleapp/about/page.js中的title替换
  • 来自app/layout.js的所有openGraph字段都被app/about/page.js继承,因为app/about/page.js没有设置openGraph元数据。

动态图片生成

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

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

app/about/route.js
import { ImageResponse } from 'next/og'
 
export async function GET() {
  return new ImageResponse(
    (
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          textAlign: 'center',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        Hello world!
      </div>
    ),
    {
      width: 1200,
      height: 600,
    }
  )
}

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

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

需要了解:

  • Vercel OG Playground中提供了示例。
  • ImageResponse使用@vercel/ogSatori和Resvg将HTML和CSS转换为PNG。
  • 仅支持Edge Runtime。默认的Node.js运行时将无法工作。
  • 仅支持flexbox和部分CSS属性。高级布局(例如display: grid)将无法工作。
  • 最大捆绑包大小为500KB。捆绑包大小包括您的JSX、CSS、字体、图像和任何其他资产。如果您超过限制,请考虑减小任何资产的大小或在运行时获取。
  • 仅支持ttfotfwoff字体格式。为了最大程度地提高字体解析速度,ttfotf优先于woff

JSON-LD

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

我们目前建议将JSON-LD作为<script>标签渲染在您的layout.jspage.js组件中。例如

app/products/[id]/page.tsx
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>
      {/* Add JSON-LD to your page */}
      <script
        type="application/ld+json"
        dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
      />
      {/* ... */}
    </section>
  )
}

您可以使用丰富结果测试(适用于Google)或通用的Schema标记验证器来验证和测试您的结构化数据。

您可以使用TypeScript和社区软件包(如schema-dts)键入您的JSON-LD。

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