返回笔记列表
01 · NOTES / WebGIS 开发

第 21 节 · 路由与布局(动态段/加载/错误)

2026 年 9 月 1 日

第 21 节 · 路由与布局(动态段/加载/错误)

📌 版本信息:Next.js 15.x App Router(2026-08-29 核对) 📚 来源:Next.js · Dynamic RoutesLoading/Error UI ▶️ 配套练习:examples/21-博客式结构/(文章详情动态段 + loading/error/not-found 全套——C-03c 的骨架预演)

一、这一节的目标

  1. 掌握动态段 [slug] 与 params 的接收
  2. 掌握三个约定文件:loading.js / error.js / not-found.js
  3. 会用嵌套布局组织分区(侧栏式博客结构)
  4. 完成"博客式结构"——文章站的完整路由形态

二、动态段:[slug]

// app/blog/[slug]/page.js —— 匹配 /blog/任意一段
export default function BlogPost({ params }) {
  const slug = params.slug;                 // /blog/hello-world → 'hello-world'(字符串)
  const post = getPost(slug);
  if (!post) notFound();                    // 触发 not-found.js(找不到内容的标准姿势)
  return <article>{post.title}</article>;
}
  • 多段动态:app/docs/[...slug]/page.js(捕获剩余所有段,全段动态)
  • 动态段默认使页面变为动态渲染;配合 generateStaticParams 把已知参数在构建时生成静态页(SSG,第 25 节详讲)

三、三个约定文件:体验的保险丝

文件 触发时机 作用
loading.js 该路由段的页面/数据加载期间 自动包住页面的骨架屏(React Suspense 的约定化)
error.js 该段渲染抛错时 错误兜底 UI(必须是客户端组件 "use client",含重试按钮)
not-found.js 调用 notFound() 或 404 未找到 UI
// app/blog/loading.js —— 零配置骨架屏
export default function Loading() {
  return <p>文章加载中…</p>;
}

// app/blog/error.js —— 必须客户端组件 + 提供重置(重试)
'use client';
export default function Error({ error, reset }) {
  return (
    <div>
      <p>出错了:{error.message}</p>
      <button onClick={reset}>重试</button>   {/* reset:重新渲染该段 */}
    </div>
  );
}

粒度即体验:约定文件放在哪一层就管哪一层子树——只在 blog/ 下放 loading.js,首页切换就不受影响。这正是"每段独立加载 UI"的流式渲染基础(第 25 节)。


四、动手跟练:21 · 博客式结构

配套文件夹:03-react-nextjs/examples/21-博客式结构/npm i && npm run dev

步骤:

  1. 结构:/blog(列表)→ /blog/[slug](详情,含 loading/error/not-found 全套)
  2. 完成 5 个 TODO:详情页读取 params 并匹配数据(不匹配调 notFound)、把手写 <a> 全换 next/link(体验零刷新)、给 loading.js 换成骨架条样式、error.js 的 reset 验证(故意把一条数据指向不存在的 slug)、给 blog 段加嵌套 layout(左侧"最新文章"侧栏)
  3. Network 面板观察:进入详情页时的 RSC 载荷请求(Next 的服务端组件"补丁",第 23 节揭晓原理)

通关标准:

  • /blog/hello 出文章、/blog/none 出 404、抛错出 error UI 且能重试
  • 全站导航零刷新(全 Link)
  • 能说出三个约定文件各自的触发条件与放置粒度

五、自测题

  1. params.slug 的类型?动态段让页面变成什么渲染模式?
  2. notFound() 从哪 import?调用后发生什么?
  3. error.js 为什么必须 "use client"?reset 干什么?
  4. loading.js 的底层机制是什么(React 哪个特性)?
  5. 想让"仅文章详情页有骨架屏、列表页没有",loading.js 放哪?

参考答案

  1. 字符串;动态段默认使页面动态渲染(配合 generateStaticParams 可转 SSG)。
  2. next/navigation;抛出一个特殊信号,渲染最近的 not-found.js。
  3. 错误 UI 需要交互(重试按钮 onClick);reset 重新渲染出错的该段(清错误状态重试)。
  4. React 的 Suspense——约定文件自动把该段页面包进 Suspense 边界。
  5. app/blog/[slug]/loading.js(只管动态详情段;放 blog/ 会连列表页一起包)。

六、下一步

路由体系完备 → 第 22 节:数据获取,服务端 fetch 与缓存初识。

TAGSweb