
React Router 服务端 RSC 路由与 SSR 请求分发unstable_routeRSCServerRequest 实战解析【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router导读unstable_routeRSCServerRequest是 React Router 在Data数据模式下打通 React Server ComponentsRSC与浏览器/SSR 两端的中枢它接收进入服务器的原生Request将其交给 RSC 服务器处理后对数据 / 资源请求原样代理返回序列化 RSC 载荷payload对文档请求则把载荷渲染为可下发给浏览器的 HTML 文档。读完本文你将掌握该 API 的签名、全部参数语义、典型 SSR 入口entry.ssr.tsx写法、CSP nonce 注入方式以及它内部的请求分流、重定向、流式注入与错误重试原理。该 API 目前为实验性unstable特性其完整定义位于源码 packages/react-router/lib/rsc/server.ssr.tsx可结合 RSCStaticRouter 文档、React Server Components 指南 一起阅读。它在 RSC 架构中的位置React Router 将 RSC 场景拆成三个入口详见 React Server Components how-to 中 Entry points 一节entry.rsc.tsxReact Server把请求与路由匹配并生成 RSC 载荷对应matchRSCServerRequestentry.ssr.tsxSSR Server / 请求处理处理请求、调用 RSC 服务器、在文档请求时把 RSC 载荷渲染成 HTML——routeRSCServerRequest正是这个入口的核心 APIentry.browser.tsx浏览器水合 HTML 并设置callServer以支持水合后的服务端动作。注意文档强调并不需要物理上部署两个服务器更常见的是在同一个服务器内维护两条独立的模块图——React 在生成 RSC 载荷与生成可水合的 HTML时的行为是不同的。这在 entry.rsc.tsx 示例 中有直观体现RSC 入口通过import.meta.viteRsc.loadModule加载 SSR 入口的generateHTML先调 RSC 服务器拿到Response再交给 SSR 侧渲染 HTML。函数签名async function routeRSCServerRequest({ request, serverResponse, createFromReadableStream, renderHTML, hydrate true, nonce, }: { request: Request; serverResponse: Response; createFromReadableStream: SSRCreateFromReadableStreamFunction; renderHTML: ( getPayload: () DecodedPayload, options: { nonce?: string; onError(error: unknown): string | undefined; onHeaders(headers: Headers): void; }, ) ReadableStreamUint8Array | PromiseReadableStreamUint8Array; hydrate?: boolean; nonce?: string; }): PromiseResponse各参数的核心语义如下与 routeRSCServerRequest.md 中的 Params 一节一致参数类型说明requestRequest待路由分发的原始请求浏览器或 fetch 客户端发来的那个请求。serverResponseResponse由 RSC 处理器生成、包含序列化unstable_RSCPayload的Response或部分响应。createFromReadableStreamSSRCreateFromReadableStreamFunction你的react-server-dom-xyz/client提供的createFromReadableStream用于在服务器端解码 RSC 流载荷。renderHTML(getPayload, options) ReadableStreamUint8Array把 RSC 载荷渲染为 HTML 的函数通常配合RSCStaticRouter使用。hydrateboolean默认true是否把 RSC 载荷注入进 HTML 以便客户端水合置为false可仅渲染如用于纯 SSG/ISR 输出。noncestring可选渲染 HTML 时生成的内联脚本所需的 CSP nonce。返回值返回值是标准Response对数据 / 资源请求直接返回包含 RSC 载荷unstable_RSCPayload的响应对文档请求返回渲染好的 HTML 响应text/html。典型用法自定义 SSR 入口官方示例中routeRSCServerRequest通常被封装在 SSR 入口导出的generateHTML(request, serverResponse)内。下面是根据 routeRSCServerRequest.md 与 React Server Components how-to 的 Vite 示例 整理的完整可运行写法import { createFromReadableStream } from vitejs/plugin-rsc/ssr; import * as ReactDomServer from react-dom/server.edge; import { unstable_RSCStaticRouter as RSCStaticRouter, unstable_routeRSCServerRequest as routeRSCServerRequest, } from react-router; export async function generateHTML( request: Request, serverResponse: Response, ): PromiseResponse { return await routeRSCServerRequest({ // 传入的原始请求。 request, // RSC 服务器生成的响应。 serverResponse, // React Server 的解码触点。 createFromReadableStream, // 把 router 渲染成 HTML。 async renderHTML(getPayload, options) { const payload await getPayload(); const formState payload.type render ? await payload.formState : undefined; // 可选的 bootstrap 脚本内容如内联启动配置。 const bootstrapScriptContent await import.meta.viteRsc.loadBootstrapScriptContent(index); return await ReactDomServer.renderToReadableStream( RSCStaticRouter getPayload{getPayload} /, { ...options, bootstrapScriptContent, formState, signal: request.signal, }, ); }, }); }注意三个同名参数在此各司其职getPayload()触发对 RSC 流的解码返回一个扩展版载荷 Promise。从源码看这个对象上还挂有_deepestRenderedBoundaryId供错误边界定位和formState表单状态两个访问器见 server.ssr.tsx 的getPayload实现options.nonce由routeRSCServerRequest注入的 CSP nonceoptions.onError/options.onHeaders分别用于拦截渲染错误与收集 React 生成的响应头。RSC Framework 模式下的内置默认 SSR 入口react-router/dev/config/default-rsc-entries/entry.ssr本质就是上述模板的官方实现可查看 default-rsc-entries/entry.ssr.tsx 作为对照仓库中的 playground/rsc-vite/src/entry.ssr.tsx 与 integration/helpers/rsc-vite/src/entry.ssr.tsx 也提供了真实可跑的同款入口代码。参数深入hydrate 与 noncehydrate默认true决定最终的 HTML 响应里是否注入可水合的 RSC 载荷脚本。从源码看server.ssr.tsx当hydrate为true时HTML 流会经过injectRSCPayload(serverResponseB.body, { nonce })转换把 RSC 流切分成一段段script注入/body之前当hydrate为false时HTML 直接原样输出仅追加可能的重定向 meta 标签。因此如果你要用 RSC 做SSG / ISR 纯预渲染而不需要客户端水合应关闭hydrate。nonceRSC 载荷的传输依赖 HTML 文档中的内联脚本形如(self.__FLIGHT_DATA||[]).push(...)。如果你的站点启用了 CSP就必须为这些内联脚本生成一次性 nonce。规范做法是为每个文档响应生成一个新的 nonce并同时传给routeRSCServerRequest、RSCStaticRouter以及 CSP 响应头详见下面的安全章节。底层原理请求分流与响应代理从源码实现server.ssr.tsx可看出routeRSCServerRequest的核心决策逻辑。它首先对 URL 与请求头做三类判断const url new URL(request.url); const isDataRequest isReactServerRequest(url); // url.pathname.endsWith(.rsc) const respondWithRSCPayload isDataRequest || isManifestRequest(url) || // url.pathname.endsWith(.manifest) request.headers.has(rsc-action-id); // 服务端动作Server Action请求随后数据 / 资源请求直接代理若命中上述任一判断或者serverResponse带有React-Router-Resource: true头则原样返回serverResponse——此时浏览器拿到的是纯 RSC 流由客户端侧RSCHydratedRouter配合createFromReadableStream解码并继续 SPA 导航。文档请求才渲染 HTML其余请求会进入下面的 HTML 渲染管线。这套按 URL 后缀/请求头 响应头分流的设计同时服务于框架路由懒发现manifest 请求与 Server Actionrsc-action-id是保持一次请求、一套数据的关键。重定向识别渲染前会用克隆的响应先解码一次载荷识别 React Router 特有的single-fetch 重定向状态码SINGLE_FETCH_REDIRECT_STATUS与type redirect的载荷此时会剥离编码相关头Content-Encoding、Content-Length、Content-Type、X-Remix-Response并改写为真正的Location重定向server.ssr.tsx。源码中还通过hasInvalidProtocol校验重定向地址的协议合法性避免开放重定向。流式注入与 HTML 结尾重定向HTML 渲染完成后输出Content-Type: text/html; charsetutf-8合并 React 头与 RSC 服务器头若发生了重定向会在 HTML 流flush阶段追加meta http-equivrefresh content0;url...并做 HTML 转义保证即使 HTML 已开始流式输出也能完成跳转若hydratetrueHTML 会通过injectRSCPayload把 RSC 载荷以script块形式流式写入/body之前。injectRSCPayload实现在 lib/rsc/html-stream/server.ts它会在每个事件循环 tick 聚合 HTML 分块避免在 HTML 半截块中间错误插入脚本对无法 UTF-8 解码的二进制块退化为 base64 编码的Uint8Array.from(...)同时对 payload 脚本内容做escapeScript、对 nonce 属性做escapeAttribute转义。相关行为在测试tests/rsc/html-stream-test.ts 中都有覆盖例如streams buffered HTML, RSC payload chunks, and the HTML trailer 断言输出形如htmlbodyhiscript(self.__FLIGHT_DATA||[]).push(S1:\hello\)/script/body/htmlnonce 转义测试nonce: test断言每个 payload 脚本都带转义后的nonce属性客户端中途取消cancel场景下不会产生未处理的 Promise rejection。渲染失败的一次性重试renderHTML并非一次失败就放弃。当首次渲染因边界错误抛错时源码通过decodeRedirectErrorDigest/decodeRouteErrorResponseDigest解析 React 错误 digestrouteRSCServerRequest会结合已渲染出的最深层错误边界 id_deepestRenderedBoundaryId把归一化后的status、errors合并进载荷并重新执行一次renderHTML把错误边界渲染进最终 HTML见 server.ssr.tsx 中的重试分支。若renderHTML抛出的本身就是Response例如RSCStaticRouter遇重定向载荷抛出的响应对象则直接原样返回该响应。实战CSP nonce 的安全配置默认框架入口并不生成 nonce只有在你同时下发 CSP 响应头时才需要生成。在 React Server Components how-to 的 Content Security Policy nonces 一节 给出了完整范式在entry.ssr.tsx中为每个文档请求用crypto.randomUUID()生成新 nonce并让 nonce 贯穿三个位置——routeRSCServerRequest选项、RSCStaticRouter的nonceprop以及 CSP 响应头export async function generateHTML( request: Request, serverResponse: Response, ): PromiseResponse { const nonce crypto.randomUUID(); const response await routeRSCServerRequest({ request, serverResponse, createFromReadableStream, nonce, async renderHTML(getPayload, options) { const payload getPayload(); const bootstrapScriptContent await import.meta.viteRsc.loadBootstrapScriptContent(index); return renderHTMLToReadableStream( RSCStaticRouter getPayload{getPayload} nonce{options.nonce} /, { ...options, bootstrapScriptContent, formState: await payload.formState, signal: request.signal, }, ); }, }); response.headers.set( Content-Security-Policy, script-src self nonce-${nonce}, ); return response; }这里 nonce 的分工是routeRSCServerRequest的nonce选项作用于传输 RSC 载荷的内联脚本最终由injectRSCPayload逐个写进script nonce...展开...options传入renderHTMLToReadableStream作用于 React 自身生成的脚本传给RSCStaticRouter成为Links、ScrollRestoration等 nonce-aware 组件的默认 nonce。仓库集成测试 integration/rsc-nonce-test.ts 验证了该 nonce 链路在真实 Vite 环境中的行为。文档同时提醒对静态预渲染页面应优先使用 CSP hash 或外部脚本而不是按响应生成 nonce。适用范围与限制本文 API 标注为unstable属于实验性能力可能在任何 minor/patch 版本中发生破坏性变更使用时需格外谨慎并密切跟进 CHANGELOG.md 中的相关变更。只适用于Data 模式文档头部标注[MODES: data]在 RSC Framework 模式下它已封装进框架自带/可覆盖的entry.ssr.tsx通常无需直接调用但了解其原理有助于你理解框架模式下的 SSR 输出。若无需 SSR / 客户端水合也可只取它的 HTML 生成能力用于静态站点生成此时记得关闭hydrate。结合 RSCStaticRouter 文档它负责真正把解码出的载荷渲染为 HTML 静态路由树与 matchRSCServerRequest 文档载荷的生成端你即可在自己的 Vite/自定义框架中复刻出一套完整的 RSC SSR 双模块图请求链路。【免费下载链接】react-routerDeclarative routing for React项目地址: https://gitcode.com/GitHub_Trending/re/react-router创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考