SuperAPI AI 工具集

router-core/ssr

前端与建站

查看项目

更新于 10-16 10:01

技能介绍

概述

这是 TanStack Router 的服务端渲染(SSR)能力说明。SSR API 目前为实验性,与 TanStack Start 共享内部实现,可能发生变化。官方推荐在生产环境使用 TanStack Start 进行 SSR,只有在需要与既有服务器集成时才使用手动 SSR 配置。

需要特别注意:TanStack Router 是客户端优先(client-first)的,loader 默认在客户端运行。启用 SSR 后,loader 会同时在客户端和服务端运行,而不是像 Remix/Next.js 那样只在服务端运行。因此不应照搬 Next.js 模式(getServerSideProps、App Router、服务端组件)或 Remix 模式(仅服务端的 loader 导出),TanStack Router 有自己独立的 SSR API。

两种 SSR 形态

  • 非流式(Non-streaming):整页在服务端渲染完成后,作为单个 HTML 响应一次性发送,然后在客户端进行 hydration。
  • 流式(Streaming):立即发送关键首屏内容,其余内容在解析完成后逐步流式传输。

关键行为:

  • 服务端自动使用内存历史(memory history,因为没有 window)。
  • loader 数据在服务端自动脱水(dehydrate),在客户端自动注水(hydrate)。
  • 数据序列化开箱支持 Date、Error、FormData 和 undefined。

设置:共享 Router 工厂

路由器必须在服务端和客户端以相同方式创建。需要从共享文件导出一个工厂函数,示例代码(src/router.tsx)见资料原文,使用 createTanstackRouter 与 routeTree 并声明模块注册。

非流式 SSR

服务端入口可使用 defaultRenderHandler,或使用 renderRouterToString 配合 RouterServer 实现自定义包装。客户端入口使用 hydrateRoot 与 RouterClient。相关示例代码见资料原文,涉及 createRequestHandler、defaultRenderHandler、renderRouterToString、RouterServer、RouterClient 等导入。

流式 SSR

服务端入口可使用 defaultStreamHandler,或使用 renderRouterToStream 配合 RouterServer 自定义包装。流式是自动的:当使用 defaultStreamHandler 或 renderRouterToStream 时,延迟数据(loader 中未 await 的 promise)与流式标记会自动工作。相关示例代码见资料原文。

文档头部(Head)管理

通过路由的 head 选项管理 title、meta、link 和 style 标签。在 head 区域渲染 HeadContent,在 body 中渲染 Scripts。可为根路由设置 head,也可为每条路由设置 head,子路由中同 name/property 的标签会覆盖父路由。SPA 场景(无服务端渲染 HTML)可在组件树顶部渲染 HeadContent。

Body 脚本方面,使用 scripts(与 head.scripts 分开)可在应用入口之前向 body 注入脚本,由 Scripts 组件渲染,通常放在 body 末尾。

ScriptOnce 用于 hydration 之前的脚本:在 SSR 期间渲染一个立即执行并自我移除的 script 标签,在客户端导航时不执行以避免重复。若脚本修改了 DOM(如给 html 添加 class),需要在对应元素上使用 suppressHydrationWarning。

Express 集成示例

createRequestHandler 接收 Web API Request 并返回 Web API Response。对 Express 需在两种格式之间转换。示例代码展示了从 express 的 req/res 构造 Request、调用 handler 与 renderRouterToString、把响应状态与响应头写回 res,并通过 pipeline 输出响应体的做法。

常见错误

  1. 高优先级:在 loader 中使用浏览器 API 而未做环境判断。SSR 下 loader 同时在客户端与服务端运行,window、document、localStorage 等仅在浏览器可用的 API 会在服务端报错,需用 typeof window !== 'undefined' 做环境判断。

  2. 中优先级:对服务端渲染内容使用 hash 片段。hash(#section)永远不会发送到服务端,基于 hash 的条件渲染会导致 hydration 不匹配,应改用 search params 作为服务端可见的状态。

  3. 关键:生成 Next.js、Remix 或 React Router DOM 模式。TanStack Router 不使用 getServerSideProps、getStaticProps、App Router 的 page.tsx、Remix 风格的仅服务端 loader 导出,也不使用 react-router-dom。正确的文件结构是 src/routes/__root.tsx、src/routes/index.tsx、src/routes/posts/$postId.tsx;路由相关的 Link、useNavigate、useRouter、useLocation、redirect 等均来自 @tanstack/react-router。若代理输出中出现 src/pages/、app/layout.tsx、react-router-dom 等,说明在为错误的框架生成代码,构建会失败或产生重复且冲突的 / 路由。

客户端优先 loader 与 SSR 的张力

TanStack Router 的 loader 设计上是客户端优先的。启用 SSR 后它们会在两种环境中运行,这意味着:浏览器 API 默认可用(仅客户端时)但在 SSR 下会中断;数据库访问不属于 loader(不同于 Remix/Next),应使用 API 路由;若需要配合 SSR 的服务端专属数据逻辑,应使用 TanStack Start 的服务端函数。

交叉引用

  • router-core/data-loading:SSR 会改变 loader 的执行位置。
  • compositions/router-query:与 TanStack Query 的 SSR 脱水/注水。

限制

SSR API 为实验性,可能变化;生产环境推荐使用 TanStack Start;手动 SSR 仅建议用于与既有服务器集成。资料未提供具体版本号、性能数据或收费信息。