N02第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

App Router 与页面

App Router 与页面 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

App Router 的核心约定是「目录即 URL」。读完你应该能建出课程列表页和详情页,用动态段和 notFound() 处理无效课程,并用 Link 组件把页面连成可导航的流程。

问题场景

学习工作台需要两个页面:/courses 展示课程列表,/courses/react-state 展示某门课详情。没有路由配置文件可写——在 App Router 里,建目录就是建路由。

核心解释

目录结构直接映射 URL:

app/
├── page.tsx                 → /
└── courses/
    ├── page.tsx             → /courses
    └── [slug]/
        └── page.tsx         → /courses/react-state、/courses/anything

[slug] 是动态段:方括号里的名字成为参数,匹配 URL 中对应的那一段。页面组件从 Promise 里 await 出参数——这是 Next.js 15+ 的约定,params 不是普通对象:

// app/courses/[slug]/page.tsx
import { notFound } from "next/navigation";

const COURSES: Record<string, { title: string; desc: string }> = {
  "react-state": { title: "React 状态管理", desc: "快照、reducer 与 Context" },
  "app-router": { title: "App Router 入门", desc: "目录即 URL" },
};

export default async function CoursePage({
  params,
}: {
  params: Promise<{ slug: string }>;
}) {
  const { slug } = await params;
  const course = COURSES[slug];
  if (!course) notFound(); // 渲染最近的 not-found 边界

  return (
    <main>
      <h1>{course.title}</h1>
      <p>{course.desc}</p>
    </main>
  );
}

无效课程调用 notFound(),它会渲染 not-found 边界。根级兜底放在 app/not-found.tsx,也可以在嵌套目录里放更近的兜底。配套建一个:

// app/not-found.tsx
import Link from "next/link";

export default function NotFound() {
  return (
    <main>
      <h2>404 · 没有这门课</h2>
      <Link href="/courses">回课程列表</Link>
    </main>
  );
}

站内导航用 Link 而不是 <a>:Link 做客户端路由切换(不整页刷新、预取目标页面),<a> 会触发完整加载,丢掉所有客户端状态。外部地址才用 <a>。

补全列表页就有了完整闭环:app/courses/page.tsx 遍历数据渲染每个 <Link>,详情页查不到就落进自定义 404。路由、兜底、导航三件事一次配齐。

示例拆解

本课要落地的完整文件清单:

  • app/courses/page.tsx:列表页,遍历 COURSES 数据为每门课渲染 <Link href={"/courses/" + slug}>;
  • app/courses/[slug]/page.tsx:详情页,await params 后查数据,查不到 notFound();
  • app/not-found.tsx:404 兜底,含返回列表的 Link;
  • 验证路径:首页 → 列表页(点链接切换,观察页面无整页刷新)→ 详情页;再手动输入 /courses/不存在 看自定义 404。

动手任务

在项目里完成三个小改动:

  1. 建 /courses 列表页和 /courses/[slug] 详情页,数据先用内存对象。
  2. 给详情页加 notFound() 分支,并自定义 app/not-found.tsx。
  3. 在首页加一个指向 /courses 的 Link,并在浏览器里对比 Link 和 <a> 的导航差异(观察是否整页刷新)。

常见错误

  • 把 params 当普通对象直接用:params.slug 在新版 Next.js 里拿不到值——params 是 Promise,必须 await params 再解构。
  • 忘了写 page.tsx:目录存在但没有 page.tsx 就不是路由,访问会 404;每个 URL 叶子节点都需要 page。
  • 站内导航用了 <a href>:功能上能跳,但丢掉了客户端路由、预取和状态保留;站内一律 Link。
  • notFound() 之后还在往下渲染:notFound() 抛出特殊错误中断渲染,它后面的代码不会执行;类型收窄后安心写主逻辑即可。

检查与复盘

完成下方检查题确认理解。自查清单:

  • 能画出目录结构和对应 URL 的映射表,包括动态段
  • 详情页对无效参数有 notFound() 兜底,自定义 404 可见
  • 站内链接全部是 Link,导航切换不整页刷新

参考资料

检查题共 1 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1多选简单(多选,全部选对才算正确)

项目里有 app/blog/[slug]/page.tsx。下列哪些 URL 会被这个动态路由匹配?(多选)

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...