mcpField
S
streaming-ssr

streaming-ssr

github.com/patternsdev开发257895 次安装28 周前更新
代码仓库

关于 streaming-ssr

面向 React 开发者的流式服务端渲染指南,介绍现代 React API。它说明如何先发送页面外壳,再逐步传输准备就绪的较慢内容。

主要功能

  1. 01逐步传输 HTML
  2. 02提前发送页面外壳
  3. 03支持 Suspense 边界
  4. 04处理网络背压
  5. 05React 流式 API 指导

适用场景

  1. 01减少大型页面的初始绘制延迟
  2. 02在较慢区块前发送关键页面内容
  3. 03网络拥塞时保持渲染响应

安装

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

$npx skills add https://github.com/patternsdev/skills --skill streaming-ssr

安装后在 Claude Code 中输入/streaming-ssr确认已加载。

详细介绍

流式服务端渲染

我们可以通过_流式传输_应用的内容,在服务端渲染应用的同时缩短交互时间(Time To Interactive)。无需生成一个包含当前导航所需标记的大型 HTML 字符串,我们可以先发送外壳,再稍后流式传输较慢的部分。客户端一收到第一批 HTML 数据块,就可以开始解析并绘制页面。

现代 React 流式渲染会在 Node 运行时使用 renderToPipeableStream(),或在 Web Stream 运行时使用 renderToReadableStream(),然后在客户端使用 hydrateRoot() 对响应进行水合。

适用场景

  • 如果你希望在 HTML 生成时逐步发送,以改善 TTFB 和 FCP,请使用此方式
  • 对于大型页面,此方式很有帮助,因为等待完整 HTML 会延迟首次绘制

不适用场景

  • 托管环境不支持流式响应时(某些无服务器平台会缓冲完整响应)
  • 对于简单的静态页面,如果 HTML 足够小,流式传输不会带来明显改善
  • 当技术栈中的中间件或反向代理会缓冲响应,从而抵消流式传输的优势时

说明

  • 使用 renderToPipeableStream(React 18+),而不是已弃用的 renderToNodeStream
  • 将流式传输与 Suspense 边界结合使用,以便在较慢的部分加载时流式传输部分内容
  • 使用 onShellReady 回调,在关键外壳准备就绪后开始流式传输
  • 使用 onError 回调处理流式传输错误

详情

初始 HTML 会与 App 组件的数据块一起发送到响应对象:

<!DOCTYPE html>
<html>
  <head>
    <title>Cat Facts</title>
    <link rel="stylesheet" href="/style.css" />
    <script type="module" defer src="/build/client.js"></script>
  </head>
  <body>
    <h1>Stream Rendered Cat Facts!</h1>
    <div id="approot"></div>
  </body>
</html>

现代 React 在 Node 上使用 renderToPipeableStream 进行流式渲染:

import { renderToPipeableStream } from "react-dom/server";

app.use("*", (request, response) => {
  let didError = false;

  const { pipe } = renderToPipeableStream(<App />, {
    bootstrapScripts: ["/build/client.js"],
    onShellReady() {
      response.statusCode = didError ? 500 : 200;
      response.setHeader("Content-Type", "text/html");
      pipe(response);
    },
    onError(error) {
      didError = true;
      console.error(error);
    },
  });
});

如果我们使用 renderToString 在服务端渲染 App 组件,就必须等到整个树渲染完成后才能发送响应。采用流式传输时,服务端可以提前刷新外壳,并在较慢的内容准备就绪后继续发送。

概念

与渐进式水合类似,流式传输是另一种可用于提升 SSR 性能的渲染机制。顾名思义,流式传输意味着 HTML 会在生成时分块从 Node 服务端传输到客户端。即使页面很大,客户端也能更早开始接收 HTML“字节”,因此 TTFB 会缩短且相对稳定。所有主流浏览器都会更早开始解析和渲染流式内容或部分响应。由于渲染是渐进式的,因此可以加快 FP 和 FCP。

流式传输能够很好地应对网络背压。如果网络拥塞,无法继续传输更多字节,渲染器会收到信号并暂停流式传输,直到网络恢复畅通。因此,服务端使用的内存更少,也能更灵敏地应对 I/O 状况。这使 Node.js 服务端能够同时渲染多个请求,并防止较重的请求长时间阻塞较轻的请求。结果是,即使在严苛的环境下,网站也能保持响应。

用于流式传输的 React

React 18 引入了现代流式 API:

  1. renderToPipeableStream(element, options),用于 Node.js HTTP 响应。
  2. renderToReadableStream(element, options),用于边缘环境等 Web Streams 运行时。

这些 API 支持 Suspense 边界、onShellReady、onAllReady,以及在客户端通过 hydrateRoot() 进行渐进式水合。

外壳准备就绪后,流的输出即可开始发送字节。响应会逐步向客户端发送数据块,同时服务端继续渲染较慢的数据块。

流式 SSR — 优缺点

流式渲染旨在提升 React SSR 的速度,并带来以下优势:

  1. 性能提升: 服务端开始渲染后不久,首字节就会到达客户端,因此 TTFB 优于 SSR。无论页面大小如何,TTFB 都更稳定。客户端一收到 HTML 就可以开始解析,因此 FP 和 FCP 也更低。

  2. 处理背压: 流式渲染能够很好地应对网络背压或拥塞,即使在条件严苛时也能让网站保持响应。

  3. 支持 SEO: 搜索引擎爬虫可以读取流式响应,因此网站可以进行 SEO 优化。

需要注意的是,流式渲染的实现并不是将 renderToString 简单地查找并替换为 renderToPipeableStream()。在某些情况下,适用于 SSR 的代码可能无法直接用于流式渲染:

  1. 某些框架会使用 server-render-pass 生成标记,并需要在 SSR 片段之前将其添加到文档中。例如,某些框架会动态确定要添加到页面的 CSS,并将其放入前置的 <style> 标签中。

  2. 在某些代码中,会使用 renderToStaticMarkup 生成页面模板,并嵌入 renderToString 调用来生成动态内容。由于这些情况下需要的是与组件对应的字符串,因此无法将其替换为流。例如:

res.write("<!DOCTYPE html>");

res.write(renderToStaticMarkup(
 <html>
   <head>
     <title>My Page</title>
   </head>
   <body>
     <div id="content">
       { renderToString(<MyPage/>) }
     </div>
   </body>
 </html>);

流式渲染和渐进式水合都可以帮助弥合纯 SSR 与 CSR 体验之间的差距。

来源

评论

支持 Markdown