返回文章列表

文章

关于客户端状态管理 vs URL 驱动 SSR/路由

目录
  1. 一、组件间状态传递(Prop/Context/Store)
  2. 原理
  3. 使用方式
  4. 优点
  5. 缺点
  6. 二、URL 驱动(Query Params)
  7. 原理
  8. 使用方式
  9. 优点
  10. 缺点
  11. 三、适用场景对比
  12. 结论

一、组件间状态传递(Prop/Context/Store)#

原理#

  • 搜索词、标签、分页等状态存储在父组件或全局状态里。
  • SearchBox 修改状态 → 父组件或全局状态更新 → PageGrid 组件重新渲染。
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 }),
}));

优点#

  1. 响应快,UI 更新立即可见。
  2. 内部状态即可管理,无需 URL 操作。

缺点#

  1. SSR 无法获取客户端状态:
    • 服务器渲染 Page.tsx 时,无法访问客户端状态(Prop/Store)。
  2. 刷新/分享会丢失状态:
    • 用户刷新页面后,状态恢复为默认值。
  3. 分页和搜索联动复杂:
    • 需要在全局状态中保存页码、搜索词等,增加管理成本。

二、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`);

优点#

  1. SSR 完全支持:
    • 服务器可以直接读取 URL 渲染正确数据。
  2. 刷新和分享都生效:
    • URL 本身承载状态,刷新或复制链接不丢失。
  3. 分页/搜索/标签统一管理:
    • 不需要额外全局状态。
  4. SEO 友好:
    • 每个搜索页面都有唯一 URL,可被搜索引擎索引。

缺点#

  1. 小范围客户端状态变化(如输入框文字)需要单独维护:
    • 可以用 local state 控制输入框,但提交时更新 URL。
  2. 页面跳转会触发完整渲染:
    • 可通过 Next.js 的 Suspense 或局部客户端组件优化。

三、适用场景对比#

特性组件状态传递URL 传参
SSR 支持
刷新/分享
状态复杂度
SEO 友好
客户端响应速度中(可优化)

结论#

  • 客户端状态传递:适合完全前端渲染、无需 SSR 的场景,例如单页面应用的即时搜索。
  • URL 传参:适合 SSR + 数据驱动页面 + 需要分享/刷新保留状态 的场景。 ✅ 在 Next.js App Router + Server Component 场景下,这是标准做法。