页面元数据与索引
页面元数据与索引 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
课程页标题全是站名、描述永远同一句、无效课程返回 500——这些会让页面在浏览器标签、分享卡片和搜索引擎里失去身份。本课给课程和项目页配上准确的标题、描述和稳定链接,并理清「页面元数据」与「正文索引」的关系。
问题场景
你把课程页链接发到群里,预览卡片显示「ReactLab」加一句通用 slogan——对方看不出这是哪门课。标签页里开了五个课程页,标题全都一样,切换全靠猜。这些信息不是正文,是页面的「名片」:title、description、canonical URL。
核心解释
静态元数据:导出 metadata 对象。 全局兜底放在根 layout,子页面各自覆盖:
// app/layout.tsx(节选)
import type { Metadata } from "next";
export const metadata: Metadata = {
title: { default: "ReactLab", template: "%s | ReactLab" },
description: "个人前端学习工作台",
};
动态元数据:generateMetadata 按内容生成。 课程页的标题描述来自数据本身:
// app/courses/[slug]/page.tsx(节选)
import type { Metadata } from "next";
import { notFound } from "next/navigation";
interface Props {
params: Promise<{ slug: string }>;
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const { slug } = await params;
const course = await getCourse(slug);
if (!course) return { title: "课程不存在" };
return {
title: course.title, // 套进模板:流式渲染与加载边界 | ReactLab
description: course.summary,
alternates: { canonical: `/courses/${slug}` },
};
}
export default async function CoursePage({ params }: Props) {
const { slug } = await params;
const course = await getCourse(slug);
if (!course) notFound(); // 渲染层同样要兜住
return <article>{/* 课程正文 */}</article>;
}
三条要点:
- 标题模板:layout 声明 template,子页只写自己的名字,站名自动拼接——标签页里一眼分清五个课程页。
- 稳定链接:slug 是课程的身份,永不改变;canonical 告诉索引器「这个地址是正身」,带查询参数的副本不会被当成重复页面。
- 无效页面走 notFound():数据查不到就触发 404 约定文件,返回明确的 404 状态码——比渲染一个内容为空的 200 页面诚实得多,搜索引擎也能正确丢弃死链。
关于索引再澄清一层:搜索引擎读的是渲染后的 HTML——元数据提供标题与描述,正文提供内容本身,两者都在服务端渲染输出里,这正是 App Router 服务端组件的天然优势。内容站还可以用 sitemap.ts / robots.ts 以代码方式声明「哪些页面值得收录」。
示例拆解
本机验证:
- 打开两门不同的课程页,标签页标题分别是「X | ReactLab」「Y | ReactLab」。
- 执行
curl -s localhost:3000/courses/n07-server-data-access | grep -o "<title>[^<]*</title>"——输出含课程名。 - 访问一个不存在的 slug——得到 404 页面,
curl -s -o /dev/null -w "%{http_code}"输出 404。 - 查看页面源码,确认 description 与课程 summary 一致。
关键点:元数据与正文必须来自同一次数据查询,否则「卡片说 A、页面演 B」的不一致早晚会发生。
动手任务
在本机工程里完成:
- 给根 layout 配 title template,给两门课程页配 generateMetadata,验证标签页标题差异。
- 给项目详情页补 canonical 与 description,用 curl 检查最终 HTML。
- 故意访问错误 slug,确认走 not-found 页面且状态码是 404 而非 200。
常见错误
- generateMetadata 与页面各查一次数据、结果不一致:元数据说有这课,正文却渲染空;两处应复用同一个查询函数。
- 在 generateMetadata 里抛异常:整页 500;数据缺失应返回占位元数据或调用 notFound()。
- 标题没配 template:每页手工拼站名,漏拼就千篇一律。
- 动态页面不兜 notFound():无效 slug 渲染 200 空页,搜索引擎索引一堆空壳。
检查与复盘
自查清单:
- 每个课程、项目页的标签标题唯一且含自身名字
- 主要页面的 description 准确,分享卡片可辨认
- 无效链接返回 404 状态码与友好页面
参考资料
- Next.js 文档:generateMetadata —— 元数据生成 API 全解
- Next.js 文档:not-found 文件约定 —— 404 的约定处理
- Next.js 文档:Metadata 和 OG 图片 —— 元数据体系总览
读完这一课了?
标记完成后,工作台会安排接下来的学习。