第 6 课:Metadata 与文件约定 — SEO 的第一步

1 阅读4分钟

模块一:破冰入门 | 前置要求:第 5 课 覆盖文档:Metadata and OG images(基础部分) 时长:90 分钟


一、为什么 Metadata 重要

Metadata(元数据)是搜索引擎和社交媒体了解你页面内容的主要方式:

  • SEO:搜索引擎根据 title/description 展示搜索结果
  • 社交分享:Facebook/Twitter/微信根据 OG 图片和标题生成预览卡片
  • 浏览器行为:favicon、主题色、viewport 影响浏览器展示

Next.js 提供三种方式定义 metadata:

  1. 静态 metadata 对象导出
  2. 动态 generateMetadata 函数
  3. 文件约定(favicon.ico、opengraph-image 等)

二、默认 Meta 标签

Next.js 始终自动添加两个基础标签:

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

即使你不定义任何 metadata,这两个也会存在。


三、静态 Metadata

3.1 基本用法

layout.tsxpage.tsx 中导出 metadata 对象:

// app/layout.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: '我的网站',
  description: '用 Next.js 构建的个人网站',
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh-CN">
      <body>{children}</body>
    </html>
  )
}

生成的 HTML:

<head>
  <title>我的网站</title>
  <meta name="description" content="用 Next.js 构建的个人网站" />
</head>

3.2 页面级覆盖

子页面可以覆盖父布局的 metadata:

// app/blog/page.tsx
import type { Metadata } from 'next'

export const metadata: Metadata = {
  title: '博客文章',             // 覆盖根布局的 title
  description: '阅读最新的技术文章',  // 覆盖根布局的 description
}

export default function BlogPage() {
  return <h1>博客</h1>
}

3.3 常用字段

export const metadata: Metadata = {
  // 基础
  title: '页面标题',
  description: '页面描述',

  // 模板标题(子页面继承)
  title: {
    template: '%s | 我的网站',    // 子页面: "博客 | 我的网站"
    default: '我的网站',          // 没有 title 时的默认值
  },

  // Open Graph(社交分享)
  openGraph: {
    title: '分享标题',
    description: '分享描述',
    images: ['/og-image.jpg'],
    type: 'website',
  },

  // Twitter Card
  twitter: {
    card: 'summary_large_image',
    title: 'Twitter 标题',
    description: 'Twitter 描述',
    images: ['/twitter-image.jpg'],
  },

  // 其他
  keywords: ['Next.js', 'React', '前端'],
  authors: [{ name: '作者名' }],
  robots: {
    index: true,
    follow: true,
  },
}

3.4 title 模板

在根布局中定义 title.template,子页面的 title 会自动套用:

// app/layout.tsx
export const metadata: Metadata = {
  title: {
    template: '%s | 我的网站',
    default: '我的网站',
  },
}

// app/blog/page.tsx
export const metadata: Metadata = {
  title: '博客',  // 最终渲染为 "博客 | 我的网站"
}

// app/about/page.tsx
export const metadata: Metadata = {
  title: '关于',  // 最终渲染为 "关于 | 我的网站"
}

四、动态 Metadata 预览

动态 metadata 将在第 16 课详细讲解,这里先看一个简单示例:

// app/blog/[slug]/page.tsx
import type { Metadata } from 'next'

// 根据路由参数动态生成 metadata
export async function generateMetadata({
  params,
}: {
  params: Promise<{ slug: string }>
}): Promise<Metadata> {
  const { slug } = await params
  const post = await fetch(`https://api.example.com/posts/${slug}`).then(r => r.json())

  return {
    title: post.title,
    description: post.excerpt,
  }
}

export default async function PostPage({
  params,
}: {
  params: Promise<{ slug: string }>
}) {
  const { slug } = await params
  const post = await fetch(`https://api.example.com/posts/${slug}`).then(r => r.json())

  return <article>{post.content}</article>
}

五、文件约定

5.1 Favicon

favicon.ico 放在 app/ 根目录即可:

app/
├── favicon.ico       ← 自动作为网站图标
├── layout.tsx
└── page.tsx

支持的格式:.ico(推荐,向后兼容)

5.2 App Icons

更丰富的图标支持:

app/
├── icon.png           ← <link rel="icon">(推荐 32x32)
├── icon.svg           ← SVG 图标
├── apple-icon.png     ← Apple Touch Icon180x180)

也可以用代码动态生成:

// app/icon.tsx
import { ImageResponse } from 'next/og'

export const size = { width: 32, height: 32 }
export const contentType = 'image/png'

export default function Icon() {
  return new ImageResponse(
    (
      <div
        style={{
          fontSize: 24,
          background: '#000',
          color: '#fff',
          width: '100%',
          height: '100%',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          borderRadius: 8,
        }}
      >
        N
      </div>
    ),
    { ...size }
  )
}

5.3 Open Graph 图片

静态 OG 图片

app/
├── opengraph-image.jpg    ← 默认 OG 图片(推荐 1200x630)
├── twitter-image.jpg      ← Twitter 卡片图片

路由特定 OG 图片

app/
├── opengraph-image.jpg         ← 全站默认
└── blog/
    └── opengraph-image.jpg     ← /blog 专用(覆盖默认)

更深层的 OG 图片会覆盖上层。

5.4 robots.txt

app/
└── robots.txt

内容示例:

User-Agent: *
Allow: /
Disallow: /admin/

Sitemap: https://mysite.com/sitemap.xml

也可以用代码动态生成:

// app/robots.ts
import type { MetadataRoute } from 'next'

export default function robots(): MetadataRoute.Robots {
  return {
    rules: {
      userAgent: '*',
      allow: '/',
      disallow: '/admin/',
    },
    sitemap: 'https://mysite.com/sitemap.xml',
  }
}

5.5 sitemap.xml

静态或动态生成:

// app/sitemap.ts
import type { MetadataRoute } from 'next'

export default function sitemap(): MetadataRoute.Sitemap {
  return [
    {
      url: 'https://mysite.com',
      lastModified: new Date(),
      changeFrequency: 'yearly',
      priority: 1,
    },
    {
      url: 'https://mysite.com/blog',
      lastModified: new Date(),
      changeFrequency: 'weekly',
      priority: 0.8,
    },
  ]
}

5.6 manifest.json(PWA)

// app/manifest.ts
import type { MetadataRoute } from 'next'

export default function manifest(): MetadataRoute.Manifest {
  return {
    name: '我的应用',
    short_name: 'MyApp',
    start_url: '/',
    display: 'standalone',
    background_color: '#ffffff',
    theme_color: '#000000',
    icons: [
      {
        src: '/icon-192.png',
        sizes: '192x192',
        type: 'image/png',
      },
    ],
  }
}

六、文件约定总览

文件用途支持格式
favicon.ico网站图标.ico
icon浏览器图标.ico .jpg .jpeg .png .svg 或 .tsx 动态生成
apple-iconApple Touch Icon.jpg .jpeg .png 或 .tsx 动态生成
opengraph-imageOG 分享图片.jpg .jpeg .png .gif 或 .tsx 动态生成
twitter-imageTwitter 卡片图片.jpg .jpeg .png .gif 或 .tsx 动态生成
sitemap.xml站点地图.xml 或 .ts 动态生成
robots.txt爬虫规则.txt 或 .ts 动态生成
manifestPWA 清单.json 或 .ts 动态生成

七、课后练习

练习 1:基础 SEO(基础)

  1. 在根布局添加 title 模板和 description
  2. 为每个页面添加各自的 title
  3. 添加 favicon.ico

练习 2:社交分享(中阶)

  1. 添加静态 OG 图片(1200x630)
  2. 配置 Open Graph 和 Twitter Card metadata
  3. 用社交分享调试工具验证

练习 3:robots 和 sitemap(高阶)

  1. 用 TypeScript 动态生成 robots.ts
  2. 用 TypeScript 动态生成 sitemap.ts(包含所有博客文章 URL)

练习 4:SEO 审计(资深)

  1. 用 Lighthouse SEO 审计检查得分
  2. 修复所有 SEO 问题
  3. 建立团队 SEO 检查清单

八、关键要点总结

  1. 三种定义方式:静态 metadata 对象、generateMetadata 函数、文件约定
  2. title 模板:根布局定义 template: '%s | 网站名',子页面自动套用
  3. metadata 继承:子路由覆盖父路由
  4. 文件约定:favicon/icon/og-image/robots/sitemap 放在 app/ 中自动识别
  5. 动态生成:.tsx/.ts 文件可以用代码生成图标、sitemap、robots
  6. OG 图片覆盖:更深层路由的 OG 图片覆盖上层
  7. 动态 metadata 和 OG 图片生成将在第 16 课深入

下一课:第 7 课:Server Components 与 Client Components