CodeWalk

renderToString 与 renderToPipeableStream(SSR 流式渲染)

作者:古法程序员 · 2026-05-30 12:55

请比较 React 中传统 SSR(renderToString)与 React 18 流式 SSR(renderToPipeableStream)在首屏性能上的差异,以及 Suspense 如何与流式渲染配合。

回答

古法程序员

renderToString 传统 SSR

import { renderToString } from 'react-dom/server';
const html = renderToString(<App />);
res.send(`<!DOCTYPE html>${html}`);
  • 问题:全量 TTFB(必须等待所有组件数据就绪才输出 HTML)
  • 瀑布流:父组件数据→渲染→子组件数据→渲染→最终 HTML
  • 不可中断:渲染过程阻塞 Node 事件循环

renderToPipeableStream(React 18 流式 SSR)

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

const { pipe } = renderToPipeableStream(<App />, {
  onShellReady() {
    res.setHeader('content-type', 'text/html');
    res.write('<div id="root">');
    pipe(res);
    res.write('</div>');
  },
  onShellError(err) { /* 降级处理 */ },
  onAllReady() { /* 全部完成 */ },
});

Suspense 与流式渲染

<Layout>
  <Navbar />  {/* 立即输出 */}
  <Suspense fallback={<Spinner />}>
    <Comments />  {/* 数据就绪后流式注入 */}
  </Suspense>
  <Footer />  {/* 立即输出 */}
</Layout>
  • 浏览器先收到带有 fallback 的 HTML
  • 当 Comments 的数据就绪后,React 发送 <script> 标签将内容注入到对应位置
  • 用户看到页面的主体部分立即显示,无需等待所有数据

性能对比: | 指标 | renderToString | renderToPipeableStream | |------|---------------|----------------------| | TTFB | 差(全量等待) | 优(立即输出 shell) | | FCP | 中等 | 优 | | 交互时间 | 相同(需水合) | 相同 | | 内存 | 高(全量内存) | 低(流式) |