mcpField
R
react-web-advanced

react-web-advanced

github.com/trancong12102开发748 次安装5 周前更新
代码仓库

关于 react-web-advanced

react-web-advanced 为基于 TanStack Router、TanStack Start 和 TanStack Virtual 的 React Web 应用提供实现模式,面向构建 Web 前端的开发者。内容涵盖路由、服务端渲染、数据加载和大型列表,并强调与 React Query 的协作方式。

主要功能

  1. 01TanStack Router 路由与 URL 状态
  2. 02TanStack Start 服务端函数与 SSR
  3. 03TanStack Virtual 虚拟化大型列表
  4. 04路由加载器与 React Query 集成
  5. 05代码拆分和 Suspense 模式

适用场景

  1. 01实现类型安全的导航和搜索参数
  2. 02在路由加载期间获取和预取数据
  3. 03渲染包含大量条目的列表
  4. 04配置 SSR 与认证路由流程

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/trancong12102/agentskills --skill react-web-advanced

安装后在 Claude Code 中输入/react-web-advanced确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
better-auth-start.md
integration.md
router.md
ssr-auth.md
start.md
testing.md
virtual.md

详细介绍

React Web Advanced:TanStack Router、Start 与 Virtual

基于 TanStack Router + Start + Virtual 技术栈构建的 React 应用的 Web 专属模式。 此技能扩展了 react-advanced(跨平台核心模式)。请先阅读该技能,了解 React Query、XState、Zustand、Zod、TanStack Form 和 TanStack Table 的约定。

Web 架构

Web 技术栈在共享核心之上增加了三个层:

层级库职责
路由 + URL 状态TanStack Router类型安全的导航、搜索参数、路由加载器
全栈边界TanStack Start服务端函数(createServerFn)、SSR、流式传输
大型列表TanStack Virtual为 1000+ 个项目进行虚拟化渲染
黄金法则:queryOptions 是唯一真实来源

只定义一次查询选项,然后在加载器、组件和缓存失效处理中统一导入:

// queries/posts.ts
export const postsQueryOptions = queryOptions({
  queryKey: ["posts"],
  queryFn: fetchPosts,
  staleTime: 30_000,
});
Router + React Query 接线

Router 将 QueryClient 接收为上下文——这是唯一的集成点:

const router = createRouter({
  routeTree,
  context: { queryClient },
  defaultPreload: "intent",
  defaultPreloadStaleTime: 0, // Let React Query manage staleness
});

declare module "@tanstack/react-router" {
  interface Register {
    router: typeof router;
  }
}

defaultPreloadStaleTime: 0 是有意设置的——若不设置,Router 会独立缓存加载器结果,导致预加载时 React Query 的 staleTime 被忽略。


路由加载器 + React Query 模式

阻塞式加载使用 ensureQueryData,非阻塞式加载使用 prefetchQuery
export const Route = createFileRoute('/posts/$postId')({
  loader: async ({ context: { queryClient }, params }) => {
    // Fire-and-forget secondary data
    queryClient.prefetchQuery(commentsQueryOptions(params.postId))
    // Block route render until critical data is ready
    await queryClient.ensureQueryData(postQueryOptions(params.postId))
  },
  component: PostDetail,
})

function PostDetail() {
  const { postId } = Route.useParams()
  // Data guaranteed in cache — instant, no loading state
  const { data: post } = useSuspenseQuery(postQueryOptions(postId))
  return <h1>{post.title}</h1>
}
避免请求瀑布

在路由加载器中使用 Promise.all 预取所有相互独立的数据:

loader: async ({ context: { queryClient }, params }) => {
  await Promise.all([
    queryClient.ensureQueryData(userQueryOptions(params.id)),
    queryClient.ensureQueryData(permissionsQueryOptions(params.id)),
  ]);
  // Fire-and-forget for non-critical
  queryClient.prefetchQuery(activityQueryOptions(params.id));
};
  • 绝不要在 useEffect 中获取本可放入路由加载器的数据
  • 默认情况下,父路由和子路由的加载器会并发运行

性能模式

React Compiler(React 19+)

启用编译器后:

  • 不要手动用 React.memo 包装组件
  • 不要为了性能手动使用 useMemo / useCallback
  • 要编写符合惯例的 React 代码——编译器会处理记忆化
  • 要确保代码遵循 React 规则(渲染期间不进行变更)

手动使用 useMemo / useCallback 仅在控制 effect 依赖项时仍有用。

Suspense 边界的位置
  • 路由级边界:在路由定义中使用 pendingComponent / errorComponent
  • 路由内部:为非阻塞数据分别使用 <Suspense> 包裹
  • 将相互依赖的查询归入同一个 <Suspense>,以便它们一同完成
  • 相互独立的查询使用单独的 <Suspense> 边界
代码拆分
  • 使用 .lazy() 或 .lazy.tsx 文件拆分路由——关键配置(加载器、参数)保留在主文件中,组件 / UI 拆分到延迟加载文件中
  • 对按需加载的重型组件(富文本编辑器、图表)使用 React.lazy
  • 机器定义会自动拆分,因为它们位于独立的 .ts 文件中

文件组织

src/
  routes/                  # TanStack Router file-based routes
    __root.tsx             # Root layout, router context type
    (auth)/                # Route group — no URL impact
    (app)/
      users/
        $userId.tsx
        $userId.lazy.tsx   # Component-only code split
        -components/       # "-" prefix excludes from route tree
  queries/                 # queryOptions definitions — one file per entity
  mutations/               # useMutation wrappers
  machines/                # XState machine definitions (pure TS, no React)
  stores/                  # Zustand stores
  serverFns/               # TanStack Start server functions
  components/
    ui/                    # Design system primitives
    shared/                # Cross-feature shared components
  lib/
    query-client.ts        # QueryClient singleton
    router.ts              # Router singleton
  test/
    setup.ts               # Vitest setup
    test-utils.tsx         # renderWithProviders
    mocks/handlers.ts      # MSW handlers

主要约定:

  • 路由专属组件使用 - 前缀目录,避免纳入路由树
  • 使用无路径路由组 (name)/ 进行组织,不影响 URL
  • .lazy.tsx 文件仅导出 component/pendingComponent/errorComponent
  • 测试文件与源文件放在一起(.test.ts / .test.tsx)

常见陷阱

  1. createFileRoute 中属性顺序错误 — 为了 TypeScript 类型推断,顺序必须是 validateSearch -> loaderDeps -> beforeLoad -> loader。 安装 @tanstack/eslint-plugin-router,并启用 create-route-property-order 规则。

  2. 在 loaderDeps 中返回完整的搜索参数 — 任何参数发生变化都会使缓存失效。 只提取 loader 使用的依赖项。

  3. 未配合 React Query 缓存使用 preload="intent" — 如果用户没有导航,预加载的数据就会被丢弃。 与 React Query 的 ensureQueryData 配合使用,以持久化缓存。

  4. 未注册路由器 — 如果没有 declare module Register,所有内容的类型都会变成 any。

  5. 忘记在 <Await> 外层包裹 <Suspense> — 没有 Suspense 祖先组件时会出现运行时错误。

  6. 未将 defaultPreloadStaleTime 设为 0 — 路由器的默认 staleTime 会在预加载期间覆盖 React Query 的 staleTime,导致返回过期数据。

  7. 在 notFoundComponent 中使用 useLoaderData — 这是无效的。请使用 Route.useParams(),或 通过 throw notFound({ data: ... }) 传递数据。

  8. 在验证认证流程前构建所有页面 — 应先端到端实现并验证认证流程(登录 → 会话 Cookie → 受保护路由守卫 → 重定向), 再构建功能页面。认证集成 Bug 会层层隐藏(深达 7 层以上)。 请参阅 references/ssr-auth.md。

  9. defaultPreload: "intent" 在悬停时触发认证检查 — 使用意图预加载时,每次悬停链接都会触发 beforeLoad。如果 beforeLoad 执行认证验证,每次悬停都会触发会话检查。调试认证时请禁用意图预加载,或让认证 检查感知缓存(获取数据前先调用 queryClient.getQueryData(sessionKey))。

  10. 无差别调用 invalidateQueries(),导致认证钩子级联触发 — 不带键过滤器调用 queryClient.invalidateQueries() 会使所有内容失效,包括 会话查询。认证响应式钩子会重新获取数据,组件会重新渲染,beforeLoad 也会再次运行。 务必将失效范围限定为特定实体的查询键。 有关查询键命名空间模式,请参阅 references/integration.md。

  11. 每次 API 变更都会导致认证 nanostores 重新渲染 — Better Auth 等库 内部使用 nanostores。每次成功的变更都会切换信号,导致所有 useSession()/useActiveOrganization() 订阅者同步重新渲染。 与 TanStack Router 结合时,这会造成无限重新渲染循环。请用 React Query 包装器替换认证 钩子。请参阅 references/better-auth-start.md。

  12. 每次客户端导航都会重新运行 beforeLoad — 如果 beforeLoad 中的会话检查调用了没有 React Query 缓存的服务器函数,每次点击链接都会触发一次服务器往返。 使用带有 staleTime 的 ensureQueryData,从缓存中提供数据。请参阅 references/ssr-auth.md。


参考文件

使用特定库时,请阅读相关参考文件:

文件阅读时机
references/router.md路由、搜索参数、加载器、代码拆分、导航
references/start.md服务器函数、SSR、中间件、部署
references/virtual.md虚拟化、动态高度、无限滚动、网格
references/integration.md路由器与 Query 的连接、认证守卫、查询键命名空间、组织数据
references/ssr-auth.mdSSR Cookie 认证、Vite 代理、CORS、路由守卫、FOUC、CF Workers
references/better-auth-start.mdBetter Auth + TanStack Start:信号、RQ 包装器、组织状态
references/testing.md测试路由器路由、renderWithProviders

评论

支持 Markdown