renderToString 与 renderToPipeableStream(SSR 流式渲染)
请比较 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 | 中等 | 优 | | 交互时间 | 相同(需水合) | 相同 | | 内存 | 高(全量内存) | 低(流式) |