跳到内容
App Router开始使用元数据和 OG 图像

如何添加元数据和创建 OG 图像

元数据 API 可用于定义应用程序元数据,以改进 SEO 和 Web 共享性,并包括

  1. 静态 metadata 对象
  2. 动态 generateMetadata 函数
  3. 特殊的文件约定,可用于添加静态或动态生成的 网站图标OG 图像

使用上述所有选项,Next.js 将自动为您的页面生成相关的 <head> 标签,可以在浏览器的开发者工具中检查这些标签。

默认字段

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

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

其他元数据字段可以使用 Metadata 对象(用于静态元数据)或 generateMetadata 函数(用于生成的元数据)定义。

静态元数据

要定义静态元数据,请从静态 layout.jspage.js 文件中导出一个 Metadata 对象。例如,要向博客路由添加标题和描述

app/blog/layout.tsx
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'My Blog',
  description: '...',
}
 
export default function Page() {}

您可以在生成元数据文档中查看可用选项的完整列表。

生成的元数据

您可以使用 generateMetadata 函数来 fetch 取决于数据的元数据。例如,要获取特定博客文章的标题和描述

app/blog/[slug]/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> {
  const slug = (await params).slug
 
  // fetch post information
  const post = await fetch(`https://api.vercel.app/blog/${slug}`).then((res) =>
    res.json()
  )
 
  return {
    title: post.title,
    description: post.description,
  }
}
 
export default function Page({ params, searchParams }: Props) {}

在幕后,Next.js 将元数据与 UI 分开流式传输,并在元数据解析后立即将其注入到 HTML 中。

记忆数据请求

在某些情况下,您可能需要为元数据和页面本身获取相同的数据。为了避免重复请求,您可以使用 React 的 cache 函数 来记忆返回值,并且只获取一次数据。例如,为了同时为元数据和页面获取博客文章信息

app/lib/data.ts
import { cache } from 'react'
import { db } from '@/app/lib/db'
 
// getPost will be used twice, but execute only once
export const getPost = cache(async (slug: string) => {
  const res = await db.query.posts.findFirst({ where: eq(posts.slug, slug) })
  return res
})
app/blog/[slug]/page.tsx
import { getPost } from '@/app/lib/data'
 
export async function generateMetadata({
  params,
}: {
  params: { slug: string }
}) {
  const post = await getPost(params.slug)
  return {
    title: post.title,
    description: post.description,
  }
}
 
export default async function Page({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)
  return <div>{post.title}</div>
}

Favicons

网站图标是代表您的站点在书签和搜索结果中的小图标。要向您的应用程序添加网站图标,请创建一个 favicon.ico 并将其添加到 app 文件夹的根目录。

Favicon Special File inside the App Folder with sibling layout and page files

您还可以使用代码以编程方式生成网站图标。有关更多信息,请参阅网站图标文档

静态 Open Graph 图像

Open Graph (OG) 图像是代表您的站点在社交媒体中的图像。要向您的应用程序添加静态 OG 图像,请在 app 文件夹的根目录中创建一个 opengraph-image.png 文件。

OG image special file inside the App folder with sibling layout and page files

您还可以通过在更深层的文件夹结构中创建 opengraph-image.png 来为特定路由添加 OG 图像。例如,要为 /blog 路由创建特定的 OG 图像,请在 blog 文件夹内添加一个 opengraph-image.jpg 文件。

OG image special file inside the blog folder

更具体的图像将优先于文件夹结构中高于它的任何 OG 图像。

也支持其他图像格式,例如 jpegpngwebp。有关更多信息,请参阅Open Graph 图像文档

生成的 Open Graph 图像

ImageResponse 构造函数允许您使用 JSX 和 CSS 生成动态图像。这对于依赖于数据的 OG 图像非常有用。

例如,要为每篇博客文章生成唯一的 OG 图像,请在 blog 文件夹内添加一个 opengraph-image.ts 文件,并从 next/og 导入 ImageResponse 构造函数

app/blog/[slug]/opengraph-image.ts
import { ImageResponse } from 'next/og'
import { getPost } from '@/app/lib/data'
 
// Image metadata
export const size = {
  width: 1200,
  height: 630,
}
 
export const contentType = 'image/png'
 
// Image generation
export default async function Image({ params }: { params: { slug: string } }) {
  const post = await getPost(params.slug)
 
  return new ImageResponse(
    (
      // ImageResponse JSX element
      <div
        style={{
          fontSize: 128,
          background: 'white',
          width: '100%',
          height: '100%',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
        }}
      >
        {post.title}
      </div>
    )
  )
}

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

须知:

  • 示例可在 Vercel OG Playground 中找到。
  • ImageResponse 使用 @vercel/ogSatori 和 Resvg 将 HTML 和 CSS 转换为 PNG。
  • 仅支持 flexbox 和 CSS 属性的子集。高级布局(例如 display: grid)将不起作用。