文章
关于客户端状态管理 vs URL 驱动 SSR/路由
一、组件间状态传递(Prop/Context/Store)#
原理#
- 搜索词、标签、分页等状态存储在父组件或全局状态里。
SearchBox修改状态 → 父组件或全局状态更新 →Page或Grid组件重新渲染。
SearchBox ──> Parent/Store ──> Page/Grid
使用方式#
- 通过
props:
<SearchBox value={searchTerm} onChange={setSearchTerm} />
<VideosGrid query={searchTerm} />
- 通过全局状态管理:
// zustand/store
const useSearchStore = create(set => ({
searchTerm: '',
setSearchTerm: term => set({ searchTerm: term }),
}));
优点#
- 响应快,UI 更新立即可见。
- 内部状态即可管理,无需 URL 操作。
缺点#
- SSR 无法获取客户端状态:
- 服务器渲染
Page.tsx时,无法访问客户端状态(Prop/Store)。
- 服务器渲染
- 刷新/分享会丢失状态:
- 用户刷新页面后,状态恢复为默认值。
- 分页和搜索联动复杂:
- 需要在全局状态中保存页码、搜索词等,增加管理成本。
二、URL 驱动(Query Params)#
原理#
- 搜索、分页、标签信息存储在 URL 查询参数。
- 服务端组件读取
searchParams,SSR 获取数据。 - 页面状态与 URL 完全同步。
URL: /videos?query=动漫&page=2
SearchBox <──> URL <──> Page/Grid
使用方式#
- Next.js app router:
export default async function Page({ searchParams }: { searchParams: { query?: string, page?: string } }) {
const query = searchParams.query || '';
const page = Number(searchParams.page) || 1;
const videos = await fetchVideos(query, page);
return <VideosGrid videos={videos} />;
}
SearchBox修改 URL:
import { useRouter } from 'next/navigation';
const router = useRouter();
router.push(`/videos?query=${searchTerm}&page=1`);
优点#
- SSR 完全支持:
- 服务器可以直接读取 URL 渲染正确数据。
- 刷新和分享都生效:
- URL 本身承载状态,刷新或复制链接不丢失。
- 分页/搜索/标签统一管理:
- 不需要额外全局状态。
- SEO 友好:
- 每个搜索页面都有唯一 URL,可被搜索引擎索引。
缺点#
- 小范围客户端状态变化(如输入框文字)需要单独维护:
- 可以用 local state 控制输入框,但提交时更新 URL。
- 页面跳转会触发完整渲染:
- 可通过 Next.js 的
Suspense或局部客户端组件优化。
- 可通过 Next.js 的
三、适用场景对比#
| 特性 | 组件状态传递 | URL 传参 |
|---|---|---|
| SSR 支持 | ❌ | ✅ |
| 刷新/分享 | ❌ | ✅ |
| 状态复杂度 | 高 | 低 |
| SEO 友好 | ❌ | ✅ |
| 客户端响应速度 | 高 | 中(可优化) |
结论#
- 客户端状态传递:适合完全前端渲染、无需 SSR 的场景,例如单页面应用的即时搜索。
- URL 传参:适合 SSR + 数据驱动页面 + 需要分享/刷新保留状态 的场景。 ✅ 在 Next.js App Router + Server Component 场景下,这是标准做法。