Menu

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

Metadata API 可用于定义你的应用程序元数据,以改善 SEO 和网络分享效果,包括:

  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
TypeScript
import type { Metadata } from 'next'
 
export const metadata: Metadata = {
  title: 'My Blog',
  description: '...',
}
 
export default function Page() {}

你可以在 generate metadata 文档中查看可用选项的完整列表。

生成的元数据

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

app/blog/[slug]/page.tsx
TypeScript
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
TypeScript
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
TypeScript
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>
}

网站图标

网站图标是代表你的网站的小图标,显示在书签和搜索结果中。要为你的应用添加网站图标,创建一个 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 Image 文档

生成的 Open Graph 图片

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

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

app/blog/[slug]/opengraph-image.ts
TypeScript
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 属性,包括弹性盒布局和绝对定位、自定义字体、文本换行、居中和嵌套图片。查看支持的 CSS 属性的完整列表

值得注意的是

  • Vercel OG Playground 中提供了示例。
  • ImageResponse 使用 @vercel/ogSatori 和 Resvg 将 HTML 和 CSS 转换为 PNG。
  • 仅支持弹性盒布局和部分 CSS 属性。高级布局(例如 display: grid)将不起作用。