模块一:破冰入门 | 前置要求:第 5 课 覆盖文档:Metadata and OG images(基础部分) 时长:90 分钟
一、为什么 Metadata 重要
Metadata(元数据)是搜索引擎和社交媒体了解你页面内容的主要方式:
- SEO:搜索引擎根据 title/description 展示搜索结果
- 社交分享:Facebook/Twitter/微信根据 OG 图片和标题生成预览卡片
- 浏览器行为:favicon、主题色、viewport 影响浏览器展示
Next.js 提供三种方式定义 metadata:
- 静态
metadata对象导出 - 动态
generateMetadata函数 - 文件约定(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.tsx 或 page.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 Icon(180x180)
也可以用代码动态生成:
// 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-icon | Apple Touch Icon | .jpg .jpeg .png 或 .tsx 动态生成 |
opengraph-image | OG 分享图片 | .jpg .jpeg .png .gif 或 .tsx 动态生成 |
twitter-image | Twitter 卡片图片 | .jpg .jpeg .png .gif 或 .tsx 动态生成 |
sitemap.xml | 站点地图 | .xml 或 .ts 动态生成 |
robots.txt | 爬虫规则 | .txt 或 .ts 动态生成 |
manifest | PWA 清单 | .json 或 .ts 动态生成 |
七、课后练习
练习 1:基础 SEO(基础)
- 在根布局添加 title 模板和 description
- 为每个页面添加各自的 title
- 添加 favicon.ico
练习 2:社交分享(中阶)
- 添加静态 OG 图片(1200x630)
- 配置 Open Graph 和 Twitter Card metadata
- 用社交分享调试工具验证
练习 3:robots 和 sitemap(高阶)
- 用 TypeScript 动态生成 robots.ts
- 用 TypeScript 动态生成 sitemap.ts(包含所有博客文章 URL)
练习 4:SEO 审计(资深)
- 用 Lighthouse SEO 审计检查得分
- 修复所有 SEO 问题
- 建立团队 SEO 检查清单
八、关键要点总结
- 三种定义方式:静态 metadata 对象、generateMetadata 函数、文件约定
- title 模板:根布局定义
template: '%s | 网站名',子页面自动套用 - metadata 继承:子路由覆盖父路由
- 文件约定:favicon/icon/og-image/robots/sitemap 放在 app/ 中自动识别
- 动态生成:.tsx/.ts 文件可以用代码生成图标、sitemap、robots
- OG 图片覆盖:更深层路由的 OG 图片覆盖上层
- 动态 metadata 和 OG 图片生成将在第 16 课深入