跳转到主要内容

renderToStaticMarkup

将 React 树渲染为静态 HTML - 类似于 renderToString,但不添加额外的 React 内部属性

核心概述

renderToStaticMarkuprenderToString 类似, 但有一个关键区别:它不会创建额外的 DOM 属性 (如 data-reactroot), 生成的 HTML 更干净、更轻量。这使它非常适合用于纯静态页面, 不需要客户端 hydration 的场景。

核心特点:

  • 不添加 React 内部属性 (data-reactroot 等)
  • 生成的 HTML 更小、更干净
  • 不支持客户端 hydration (因为没有内部属性)
  • 适用于纯静态内容生成

适用场景:

  • 生成电子邮件 HTML (邮件客户端不支持 JavaScript)
  • 生成 RSS/Atom feeds
  • 静态网站生成器 (SSG) 的非交互部分
  • 生成可嵌入其他网站的 HTML 片段
  • 预览/打印版本的内容

📧 经典用例:电子邮件

电子邮件客户端不支持 JavaScript,因此不需要 hydration。 使用 renderToStaticMarkup 生成的 HTML 更小,兼容性更好。

JavaScript
// 生成电子邮件 HTML
const emailHTML = renderToStaticMarkup(<EmailTemplate {...data} />);

技术规格

导入方式

TypeScript
import { renderToStaticMarkup } from 'react-dom/server';

函数签名

TypeScript
function renderToStaticMarkup(element: ReactNode): string

参数说明

参数类型说明
elementReactNode要渲染的 React 元素

返回值

返回一个 HTML 字符串,不包含 React 内部属性。

实战演练

1. 生成电子邮件 HTML

最常见的使用场景 - 生成电子邮件内容:

import { renderToStaticMarkup } from 'react-dom/server';
import EmailTemplate from './EmailTemplate';

type EmailData = {
  to: string;
  subject: string;
  userName: string;
  verificationUrl: string;
};

export async function sendVerificationEmail(data: EmailData) {
  const emailHTML = renderToStaticMarkup(
    <EmailTemplate userName={data.userName} verificationUrl={data.verificationUrl} />
  );

  await emailService.send({
    to: data.to,
    subject: data.subject,
    html: emailHTML,
  });
}

2. 生成 RSS Feed

为博客生成 RSS/Atom feeds:

import { renderToStaticMarkup } from 'react-dom/server';
import RSSFeed from './RSSFeed';

app.get('/rss', async (req, res) => {
  const posts = await fetchPosts();
  const rss = renderToStaticMarkup(<RSSFeed posts={posts} />);

  res.set('Content-Type', 'application/rss+xml');
  res.send(rss);
});

3. 生成可嵌入的 HTML 片段

生成可嵌入其他网站的 HTML widget:

import { renderToStaticMarkup } from 'react-dom/server';
import EmbedWidget from './EmbedWidget';

app.get('/embed/:productId', async (req, res) => {
  const product = await fetchProduct(req.params.productId);
  const widgetHTML = renderToStaticMarkup(<EmbedWidget product={product} />);

  const embedCode = `
    <script src="https://your-domain.com/widget.js"></script>
    <div id="widget-root">${widgetHTML}</div>
  `;

  res.json({ embedCode });
});

4. 静态网站生成

在构建时生成静态 HTML 页面:

TypeScript
import { renderToStaticMarkup } from 'react-dom/server';
import fs from 'fs';
import path from 'path';

async function buildStaticPages() {
  const posts = await fetchAllPosts();

  for (const post of posts) {
    // 生成静态 HTML
    const html = renderToStaticMarkup(
      <html>
        <head>
          <title>{post.title}</title>
          <meta name="description" content={post.excerpt} />
        </head>
        <body>
          <article>
            <h1>{post.title}</h1>
            <div dangerouslySetInnerHTML={{ __html: post.content }} />
          </article>
        </body>
      </html>
    );

    // 写入文件
    const filePath = path.join('dist', `${post.slug}.html`);
    fs.writeFileSync(filePath, `<!DOCTYPE html>${html}`);
  }
}

// 运行构建
buildStaticPages();

renderToString vs renderToStaticMarkup

选择合适的 API 取决于你的使用场景:

特性renderToStringrenderToStaticMarkup
React 内部属性✅ 添加❌ 不添加
支持 Hydration
HTML 大小较大 (有额外属性)较小 (干净 HTML)
使用场景需要 hydration 的 SSR (已被流式 API 取代)静态 HTML,电子邮件,RSS feeds

输出对比

HTML
// renderToString 输出
<div data-reactroot="">
  <h1>Hello World</h1>
</div>

// renderToStaticMarkup 输出 (更干净)
<div>
  <h1>Hello World</h1>
</div>

避坑指南

陷阱 1: 试图进行 Hydration

问题:renderToStaticMarkup 生成的 HTML 无法进行 hydration。

TypeScript
// ❌ 错误:试图对 renderToStaticMarkup 的输出进行 hydration
const html = renderToStaticMarkup(<App />);
document.getElementById('root').innerHTML = html;
hydrateRoot(document.getElementById('root'), <App />); // 无法匹配!

// ✅ 正确:如果需要 hydration,使用 renderToString 或流式 API
const html = renderToString(<App />);
document.getElementById('root').innerHTML = html;
hydrateRoot(document.getElementById('root'), <App />);

陷阱 2: 在交互式页面中使用

问题:用于需要 JavaScript 交互的页面。

TypeScript
// ❌ 错误:用于交互式应用
const html = renderToStaticMarkup(<InteractiveApp />);
// 用户点击按钮不会有任何反应

// ✅ 正确:使用 renderToString 或流式 API
const html = renderToString(<InteractiveApp />);
hydrateRoot(document, <InteractiveApp />);

延伸阅读

renderToStaticMarkup - React Server APIs - React 文档