renderToString
将 React 树渲染为 HTML 字符串 - 传统 SSR 方法
核心概述
renderToString 是 React 最早的服务端渲染 API, 用于将 React 树渲染为 HTML 字符串。它在 React 18 之前是 SSR 的标准方法, 但现在已被流式渲染 API (renderToPipeableStream 和 renderToReadableStream) 取代。
⚠️ 不推荐使用:renderToString 不支持流式传输, 必须等待整个应用渲染完成后才发送 HTML。这导致较差的用户体验和性能。 新项目应该使用 renderToPipeableStream 或 renderToReadableStream。
⚠️ 已弃用的 API
- • 不支持流式传输:必须等待整个应用渲染完成
- • 较差的 TTFB:首字节时间更长
- • 无 Suspense 支持:无法充分利用 Suspense 的流式特性
- • 仅用于兼容:维护旧项目时使用,新项目避免使用
唯一的使用场景:维护无法升级到 React 18 的旧项目, 或在不支持流式传输的环境中运行。
技术规格
导入方式
TypeScript
import { renderToString } from 'react-dom/server';函数签名
TypeScript
function renderToString(element: ReactNode): string参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
element | ReactNode | 要渲染的 React 元素 |
返回值
返回一个 HTML 字符串,包含渲染后的 React 树。
实战演练
1. 基础用法
最简单的 renderToString 用法:
TypeScript
import express from 'express';
import { renderToString } from 'react-dom/server';
import App from './App';
const app = express();
app.get('/', (req, res) => {
const html = renderToString(<App />);
res.send(`
<!DOCTYPE html>
<html>
<head>
<title>My App</title>
</head>
<body>
<div id="root">${html}</div>
<script src="/client.js"></script>
</body>
</html>
`);
});
app.listen(3000);2. 结合数据获取 (传统方法)
在渲染前获取所有数据:
TypeScript
import express from 'express';
import { renderToString } from 'react-dom/server';
import App from './App';
const app = express();
app.get('/', async (req, res) => {
// ❌ 必须等待所有数据完成才能开始渲染
const [user, posts] = await Promise.all([
fetchUser(req.session.userId),
fetchPosts(),
]);
const html = renderToString(<App user={user} posts={posts} />);
res.send(`
<!DOCTYPE html>
<html>
<head><title>My App</title></head>
<body>
<div id="root">${html}</div>
<script>
window.__INITIAL_DATA__ = ${JSON.stringify({ user, posts })};
</script>
<script src="/client.js"></script>
</body>
</html>
`);
});迁移指南
如果你的项目正在使用 renderToString,强烈建议升级到流式渲染 API:
迁移到 renderToPipeableStream (Node.js)
TypeScript
// ❌ 旧代码:renderToString
import { renderToString } from 'react-dom/server';
app.get('/', (req, res) => {
const html = renderToString(<App />);
res.send(`<!DOCTYPE html><html><body><div id="root">${html}</div>...</body></html>`);
});
// ✅ 新代码:renderToPipeableStream
import { renderToPipeableStream } from 'react-dom/server';
app.get('/', (req, res) => {
const stream = renderToPipeableStream(<App />, {
onShellReady() {
res.statusCode = 200;
res.setHeader('Content-type', 'text/html');
res.write('<!DOCTYPE html><html><body><div id="root">');
stream.pipe(res);
},
});
});迁移到 renderToReadableStream (Edge Runtime)
TypeScript
// ❌ 旧代码:renderToString
import { renderToString } from 'react-dom/server';
const html = renderToString(<App />);
return new Response(html, {
headers: { 'Content-Type': 'text/html' },
});
// ✅ 新代码:renderToReadableStream
import { renderToReadableStream } from 'react-dom/server';
const stream = await renderToReadableStream(<App />);
return new Response(stream, {
headers: { 'Content-Type': 'text/html' },
});性能对比
| 指标 | renderToString | 流式渲染 API |
|---|---|---|
| TTFB | 较慢 (等待完整渲染) | 更快 (立即发送 shell) |
| LCP | 较慢 | 更快 |
| Suspense 支持 | ❌ | ✅ |
| 流式传输 | ❌ | ✅ |