动态参数与搜索参数
动态参数与搜索参数 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
路由携带的两类参数要分清:路径里的动态段(params)标识「哪门课」,问号后面的搜索参数(searchParams)表达「怎么筛、翻到哪」。读完你应该能 await 两者、用链接承载筛选分页,并验证刷新和分享后状态完整恢复。
问题场景
课程列表页要支持「按方向筛选 + 分页」,比如 /courses?track=react&page=2。第一反应是用 useState 存筛选状态——能用,但刷新归零、分享出去就是裸链接,对方看到的是默认列表。筛选和分页本质是「页面从哪读」的输入,写进 URL 才对。
核心解释
服务端页面组件同时拿到两类参数,两者都是 Promise,都要 await:
// app/courses/page.tsx
type Props = {
params: Promise<{}>; // 路径参数:/courses/react 的 react
searchParams: Promise<Record<string, string | string[] | undefined>>;
};
export default async function CoursesPage({ params, searchParams }: Props) {
const { track = "all" } = await searchParams; // ?track=react&page=2
const page = Number((await searchParams).page ?? "1");
const filtered = track === "all" ? COURSES : COURSES.filter(c => c.track === track);
// 按 page 切片后渲染……
}
关键认知:searchParams 变化 = 导航。点一个带新查询参数的 Link,Next.js 向服务端重新请求这个页面的渲染结果,组件带着新参数重渲染。状态在 URL 里,所以:
- 刷新页面,服务端从同一个 URL 读到同样的参数——状态恢复;
- 复制链接发给别人,对方打开看到的就是你筛选后的列表;
- 浏览器前进后退,参数随之变化,列表跟着变。
生成筛选链接的写法(服务端组件里直接算):
<div>
{["all", "react", "nextjs"].map(t => (
<Link
key={t}
href={t === "all" ? "/courses" : `/courses?track=${t}&page=1`}
aria-current={track === t ? "true" : undefined}
>
{t}
</Link>
))}
</div>
客户端组件里则用 useSearchParams() 读当前查询参数,配合 router.push 或 Link 拼新地址——两条路按组件位置选,别在服务端组件里用 Hook。
示例拆解
落地清单与验证:
- 列表页同时消费 track(筛选)和 page(分页)两个查询参数,越界 page 回落到 1;
- 每个筛选项是一个 Link,当前项有高亮标记;
- 分页条也是 Link:
?track=react&page=2,保留当前筛选只翻页; - 验证一:点筛选和翻页后按 F5 刷新,列表还在同一筛选、同一页;
- 验证二:把当前地址复制到新标签页打开,状态一致;
- 验证三:详情页
/courses/[slug]用 await params 读课程标识,确认两类参数分工明确。
动手任务
在项目里完成三个小改动:
- 列表页实现 track 筛选 + page 分页,全部由查询参数驱动。
- 给「下一页 / 上一页」做成保留筛选的 Link,第一页隐藏「上一页」。
- 在浏览器直接改 URL 里的 page=99,确认页面回落到合法值而不是报错。
常见错误
- searchParams 不 await 直接点属性:拿到的是 Promise,读出来 undefined;Next.js 15+ 必须 await。
- 用 useState 存筛选分页:刷新即丢、链接不可分享;能进 URL 的 UI 状态尽量进 URL。
- 在服务端组件里用 useSearchParams:它是客户端 Hook;服务端读参数用 await searchParams。
- 参数类型想当然:查询参数永远是字符串(
?page=2来的是 "2"),且同名多值时是数组;用之前先转数字、先判断类型。
检查与复盘
完成下方检查题确认理解。自查清单:
- params 与 searchParams 的分工说得清:身份标识 vs 视图状态
- 两个参数都 await 后使用,类型处理过字符串默认值
- 刷新、新标签页打开、前进后退三条路径都验证过状态恢复
参考资料
- Next.js 文档:page.js 约定 —— params / searchParams 的类型与 Promise 约定
- Next.js 文档:useSearchParams —— 客户端组件读取查询参数
- Next.js 文档:Link 组件 —— 用链接驱动参数化导航
读完这一课了?
标记完成后,工作台会安排接下来的学习。