React服务端渲染之ServerAPI

React 服务端渲染之 Server API 深度解析

目录

  • [React DOM Server 完整 API 解析](#React DOM Server 完整 API 解析 "#react-dom-server-%E5%AE%8C%E6%95%B4-api-%E8%A7%A3%E6%9E%90")
  • [所有 renderToXX 方法详解](#所有 renderToXX 方法详解 "#%E6%89%80%E6%9C%89-rendertoxx-%E6%96%B9%E6%B3%95%E8%AF%A6%E8%A7%A3")
  • [React 18 新特性深度应用](#React 18 新特性深度应用 "#react-18-%E6%96%B0%E7%89%B9%E6%80%A7%E6%B7%B1%E5%BA%A6%E5%BA%94%E7%94%A8")
  • [Next.js Server API 体系](#Next.js Server API 体系 "#nextjs-server-api-%E4%BD%93%E7%B3%BB")
  • 高级服务端优化技术
  • 性能监控与调试
  • 最佳实践与架构模式

React DOM Server 完整 API 解析

所有 renderToXX 方法完整覆盖

React DOM Server 提供了多种渲染方法,每种都有其特定的使用场景和性能特点:

API 方法总览
方法 环境 输出类型 Hydration支持 流式渲染 React 18特性
renderToString Node.js String 有限支持
renderToStaticMarkup Node.js String
renderToNodeStream Node.js Stream 有限支持
renderToStaticNodeStream Node.js Stream
renderToPipeableStream Node.js Stream
renderToReadableStream Web Stream
渲染流程架构图
graph TB A[React Element] --> B{选择渲染方法} B --> C[renderToString] B --> D[renderToStaticMarkup] B --> E[renderToNodeStream] B --> F[renderToStaticNodeStream] B --> G[renderToPipeableStream] B --> H[renderToReadableStream] C --> C1[同步渲染HTML字符串] C --> C2[包含data-reactroot属性] C --> C3[支持客户端水合] D --> D1[纯静态HTML字符串] D --> D2[不包含React属性] D --> D3[无法水合] E --> E1[Node.js可读流] E --> E2[支持背压处理] E --> E3[包含React属性] F --> F1[静态内容流] F --> F2[更小的输出] F --> F3[无React属性] G --> G1[支持Suspense] G --> G2[选择性水合] G --> G3[并发特性] H --> H1[Web流标准] H --> H2[边缘计算友好] H --> H3[现代浏览器API] style G fill:#e1f5fe style H fill:#c8e6c9

所有 renderToXX 方法详解

1. renderToString

用途:将React元素渲染为HTML字符串,是最传统的SSR方法。

特点

  • 同步渲染,阻塞式处理
  • 生成包含React属性的HTML
  • 支持客户端hydration
  • 不支持Suspense和并发特性
javascript 复制代码
import { renderToString } from 'react-dom/server';

// 基础使用示例
function BasicApp({ title, content }) {
  return (
    <html>
      <head>
        <title>{title}</title>
      </head>
      <body>
        <div id="root">
          <h1>{title}</h1>
          <p>{content}</p>
        </div>
      </body>
    </html>
  );
}

// 完整的renderToString实现
class RenderToStringService {
  constructor() {
    this.renderCache = new Map();
    this.renderStats = {
      totalRenders: 0,
      averageTime: 0,
      errors: 0
    };
  }
  
  render(element, options = {}) {
    const startTime = Date.now();
    const cacheKey = options.cacheKey;
    
    try {
      // 检查缓存
      if (cacheKey && this.renderCache.has(cacheKey)) {
        const cached = this.renderCache.get(cacheKey);
        if (Date.now() - cached.timestamp < (options.cacheTTL || 300000)) {
          return {
            html: cached.html,
            cached: true,
            renderTime: 0
          };
        }
      }
      
      // 执行渲染
      const html = renderToString(element);
      const renderTime = Date.now() - startTime;
      
      // 更新统计
      this.updateStats(renderTime, false);
      
      // 缓存结果
      if (cacheKey) {
        this.renderCache.set(cacheKey, {
          html,
          timestamp: Date.now()
        });
      }
      
      // 返回结果
      return {
        html,
        cached: false,
        renderTime,
        size: Buffer.byteLength(html, 'utf8'),
        hasReactAttributes: html.includes('data-react')
      };
      
    } catch (error) {
      this.updateStats(Date.now() - startTime, true);
      console.error('renderToString failed:', error);
      
      // 错误降级
      return {
        html: this.getErrorFallback(error),
        cached: false,
        renderTime: Date.now() - startTime,
        error: error.message
      };
    }
  }
  
  updateStats(renderTime, isError) {
    this.renderStats.totalRenders++;
    
    if (isError) {
      this.renderStats.errors++;
    } else {
      const prevAvg = this.renderStats.averageTime;
      const count = this.renderStats.totalRenders - this.renderStats.errors;
      this.renderStats.averageTime = (prevAvg * (count - 1) + renderTime) / count;
    }
  }
  
  getErrorFallback(error) {
    return `
      <!DOCTYPE html>
      <html>
        <head>
          <title>渲染错误</title>
        </head>
        <body>
          <div id="root">
            <h1>页面渲染失败</h1>
            <p>正在加载客户端版本...</p>
            <script>
              console.error('SSR Error:', ${JSON.stringify(error.message)});
            </script>
          </div>
        </body>
      </html>
    `;
  }
  
  getStats() {
    return {
      ...this.renderStats,
      cacheSize: this.renderCache.size,
      errorRate: this.renderStats.errors / this.renderStats.totalRenders
    };
  }
}

// 使用示例
const renderService = new RenderToStringService();

// Express.js 集成
app.get('/', (req, res) => {
  const appElement = <BasicApp title="首页" content="欢迎访问我们的网站" />;
  
  const result = renderService.render(appElement, {
    cacheKey: `home:${req.headers['accept-language']}`,
    cacheTTL: 600000 // 10分钟缓存
  });
  
  res.setHeader('Content-Type', 'text/html; charset=utf-8');
  res.setHeader('X-Render-Time', result.renderTime);
  res.setHeader('X-Cached', result.cached);
  
  res.send(result.html);
});

2. renderToStaticMarkup

用途:渲染纯静态HTML,不包含React特有的DOM属性。

特点

  • 输出更干净的HTML
  • 文件体积更小
  • 不支持客户端hydration
  • 适用于静态页面生成
javascript 复制代码
import { renderToStaticMarkup } from 'react-dom/server';

// 静态页面组件
function StaticPage({ title, content, lastModified }) {
  return (
    <>
      <h1>{title}</h1>
      <article dangerouslySetInnerHTML={{ __html: content }} />
      <footer>
        <p>最后更新:{new Date(lastModified).toLocaleDateString('zh-CN')}</p>
      </footer>
    </>
  );
}

// 静态站点生成器
class StaticSiteGenerator {
  constructor(options = {}) {
    this.options = {
      outputDir: './dist',
      templatePath: './templates/base.html',
      minify: true,
      ...options
    };
  }
  
  async generatePage(component, props, outputPath) {
    try {
      // 渲染组件为静态HTML
      const componentHTML = renderToStaticMarkup(component(props));
      
      // 读取HTML模板
      const template = await this.loadTemplate();
      
      // 构建完整页面
      const fullHTML = this.buildFullPage(template, componentHTML, props);
      
      // 压缩HTML(如果启用)
      const finalHTML = this.options.minify ? this.minifyHTML(fullHTML) : fullHTML;
      
      // 写入文件
      await this.writeFile(outputPath, finalHTML);
      
      return {
        success: true,
        outputPath,
        size: Buffer.byteLength(finalHTML, 'utf8'),
        compressed: this.options.minify
      };
      
    } catch (error) {
      console.error(`Failed to generate ${outputPath}:`, error);
      return {
        success: false,
        error: error.message,
        outputPath
      };
    }
  }
  
  async loadTemplate() {
    const fs = require('fs').promises;
    return await fs.readFile(this.options.templatePath, 'utf8');
  }
  
  buildFullPage(template, content, props) {
    return template
      .replace('{{TITLE}}', props.title || '')
      .replace('{{META_DESCRIPTION}}', props.description || '')
      .replace('{{CONTENT}}', content)
      .replace('{{CANONICAL_URL}}', props.canonicalUrl || '')
      .replace('{{LANG}}', props.lang || 'zh-CN');
  }
  
  minifyHTML(html) {
    // 简化的HTML压缩
    return html
      .replace(/\s+/g, ' ')
      .replace(/>\s+</g, '><')
      .replace(/^\s+|\s+$/g, '')
      .trim();
  }
  
  async writeFile(path, content) {
    const fs = require('fs').promises;
    const dir = require('path').dirname(path);
    
    // 确保目录存在
    await fs.mkdir(dir, { recursive: true });
    
    // 写入文件
    await fs.writeFile(path, content, 'utf8');
  }
  
  // 批量生成静态页面
  async generateSite(pages) {
    const results = [];
    
    for (const page of pages) {
      const result = await this.generatePage(
        page.component,
        page.props,
        `${this.options.outputDir}${page.path}/index.html`
      );
      
      results.push({
        ...result,
        path: page.path
      });
    }
    
    // 生成站点地图
    await this.generateSitemap(pages.filter((_, i) => results[i].success));
    
    return results;
  }
  
  async generateSitemap(pages) {
    const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${pages.map(page => `
  <url>
    <loc>${page.props.canonicalUrl || `https://example.com${page.path}`}</loc>
    <lastmod>${new Date().toISOString()}</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
  </url>
`).join('')}
</urlset>`;
    
    await this.writeFile(`${this.options.outputDir}/sitemap.xml`, sitemap);
  }
}

// 使用示例
const generator = new StaticSiteGenerator({
  outputDir: './public',
  templatePath: './templates/page.html',
  minify: true
});

// 生成静态网站
const pages = [
  {
    path: '/',
    component: StaticPage,
    props: {
      title: '首页',
      content: '<p>欢迎访问我们的网站</p>',
      description: '这是网站首页',
      canonicalUrl: 'https://example.com/',
      lastModified: Date.now()
    }
  },
  {
    path: '/about',
    component: StaticPage,
    props: {
      title: '关于我们',
      content: '<p>我们是一家专业的技术公司</p>',
      description: '了解我们公司的发展历程',
      canonicalUrl: 'https://example.com/about',
      lastModified: Date.now()
    }
  }
];

generator.generateSite(pages).then(results => {
  console.log('静态站点生成完成:');
  results.forEach(result => {
    if (result.success) {
      console.log(`✓ ${result.path} (${result.size} bytes)`);
    } else {
      console.log(`✗ ${result.path} - ${result.error}`);
    }
  });
});

3. renderToNodeStream

用途:将React元素渲染为Node.js可读流,支持流式传输。

特点

  • 支持背压处理
  • 更好的内存使用
  • 包含React hydration属性
  • 逐步发送HTML内容
javascript 复制代码
import { renderToNodeStream } from 'react-dom/server';

// 流式渲染服务
class StreamingRenderService {
  constructor() {
    this.activeStreams = new Set();
    this.streamStats = {
      totalStreams: 0,
      activeCount: 0,
      bytesStreamed: 0,
      errors: 0
    };
  }
  
  createStream(element, options = {}) {
    const {
      onError = this.defaultErrorHandler,
      onComplete = this.defaultCompleteHandler,
      timeout = 30000
    } = options;
    
    try {
      // 创建渲染流
      const renderStream = renderToNodeStream(element);
      
      // 创建增强的流包装器
      const enhancedStream = this.createEnhancedStream(renderStream, {
        onError,
        onComplete,
        timeout
      });
      
      // 跟踪流状态
      this.trackStream(enhancedStream);
      
      return enhancedStream;
      
    } catch (error) {
      console.error('Failed to create render stream:', error);
      onError(error);
      return null;
    }
  }
  
  createEnhancedStream(originalStream, options) {
    const { Transform } = require('stream');
    let bytesTransferred = 0;
    let chunksCount = 0;
    const startTime = Date.now();
    
    const enhancedStream = new Transform({
      transform(chunk, encoding, callback) {
        bytesTransferred += chunk.length;
        chunksCount++;
        
        // 更新全局统计
        this.streamStats.bytesStreamed += chunk.length;
        
        // 可以在这里添加流处理逻辑
        // 例如:添加性能标记、内容修改等
        
        callback(null, chunk);
      }.bind(this),
      
      flush(callback) {
        const duration = Date.now() - startTime;
        
        options.onComplete({
          bytesTransferred,
          chunksCount,
          duration,
          averageChunkSize: bytesTransferred / chunksCount
        });
        
        callback();
      }
    });
    
    // 设置超时
    const timeoutId = setTimeout(() => {
      enhancedStream.destroy(new Error('Stream timeout'));
    }, options.timeout);
    
    // 管道连接
    originalStream.pipe(enhancedStream);
    
    // 错误处理
    originalStream.on('error', (error) => {
      clearTimeout(timeoutId);
      options.onError(error);
    });
    
    enhancedStream.on('end', () => {
      clearTimeout(timeoutId);
    });
    
    return enhancedStream;
  }
  
  trackStream(stream) {
    this.activeStreams.add(stream);
    this.streamStats.totalStreams++;
    this.streamStats.activeCount++;
    
    stream.on('end', () => {
      this.activeStreams.delete(stream);
      this.streamStats.activeCount--;
    });
    
    stream.on('error', () => {
      this.activeStreams.delete(stream);
      this.streamStats.activeCount--;
      this.streamStats.errors++;
    });
  }
  
  defaultErrorHandler(error) {
    console.error('Stream error:', error);
  }
  
  defaultCompleteHandler(stats) {
    console.log('Stream completed:', stats);
  }
  
  getStats() {
    return {
      ...this.streamStats,
      averageBytesPerStream: this.streamStats.bytesStreamed / this.streamStats.totalStreams
    };
  }
  
  // 优雅关闭所有活跃流
  async closeAllStreams() {
    const promises = Array.from(this.activeStreams).map(stream => {
      return new Promise((resolve) => {
        stream.on('end', resolve);
        stream.on('error', resolve);
        stream.destroy();
      });
    });
    
    await Promise.all(promises);
    console.log('All streams closed');
  }
}

// 大型应用组件
function LargeApp({ user, posts, comments }) {
  return (
    <html>
      <head>
        <title>用户主页 - {user.name}</title>
        <meta name="description" content={`${user.name}的个人主页`} />
      </head>
      <body>
        <div id="root">
          <header>
            <h1>欢迎,{user.name}!</h1>
            <nav>
              <a href="/">首页</a>
              <a href="/profile">个人资料</a>
              <a href="/settings">设置</a>
            </nav>
          </header>
          
          <main>
            <section className="posts">
              <h2>最新动态</h2>
              {posts.map(post => (
                <article key={post.id}>
                  <h3>{post.title}</h3>
                  <p>{post.content}</p>
                  <div className="post-meta">
                    <span>发布时间:{new Date(post.createdAt).toLocaleDateString('zh-CN')}</span>
                    <span>阅读量:{post.views}</span>
                  </div>
                </article>
              ))}
            </section>
            
            <section className="comments">
              <h2>最新评论</h2>
              {comments.map(comment => (
                <div key={comment.id} className="comment">
                  <strong>{comment.author}</strong>
                  <p>{comment.content}</p>
                  <time>{new Date(comment.createdAt).toLocaleDateString('zh-CN')}</time>
                </div>
              ))}
            </section>
          </main>
          
          <footer>
            <p>© 2024 我的网站. 保留所有权利.</p>
          </footer>
        </div>
      </body>
    </html>
  );
}

// Express.js 集成示例
const streamingService = new StreamingRenderService();

app.get('/user/:id', async (req, res) => {
  try {
    // 获取用户数据
    const [user, posts, comments] = await Promise.all([
      getUserById(req.params.id),
      getUserPosts(req.params.id, { limit: 10 }),
      getUserComments(req.params.id, { limit: 5 })
    ]);
    
    if (!user) {
      return res.status(404).send('用户不存在');
    }
    
    // 创建应用元素
    const appElement = <LargeApp user={user} posts={posts} comments={comments} />;
    
    // 创建流式渲染
    const stream = streamingService.createStream(appElement, {
      timeout: 15000,
      onError: (error) => {
        console.error(`Streaming error for user ${req.params.id}:`, error);
        if (!res.headersSent) {
          res.status(500).send('渲染失败');
        }
      },
      onComplete: (stats) => {
        console.log(`Streaming completed for user ${req.params.id}:`, stats);
      }
    });
    
    if (!stream) {
      return res.status(500).send('无法创建渲染流');
    }
    
    // 设置响应头
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.setHeader('Transfer-Encoding', 'chunked');
    
    // 管道连接到响应
    stream.pipe(res);
    
    // 处理客户端断开连接
    req.on('close', () => {
      stream.destroy();
    });
    
  } catch (error) {
    console.error('Request handling error:', error);
    res.status(500).send('服务器内部错误');
  }
});

// 健康检查端点
app.get('/health/streaming', (req, res) => {
  const stats = streamingService.getStats();
  res.json({
    status: 'healthy',
    streaming: stats,
    timestamp: new Date().toISOString()
  });
});

// 优雅关闭
process.on('SIGTERM', async () => {
  console.log('Received SIGTERM, closing streams...');
  await streamingService.closeAllStreams();
  process.exit(0);
});

4. renderToStaticNodeStream

用途:生成不包含React属性的静态HTML流。

特点

  • 纯静态内容流
  • 更小的输出体积
  • 不支持hydration
  • 适用于静态内容生成
javascript 复制代码
import { renderToStaticNodeStream } from 'react-dom/server';

// 静态内容流生成器
class StaticStreamGenerator {
  constructor(options = {}) {
    this.options = {
      compressionLevel: 6,
      enableGzip: true,
      bufferSize: 64 * 1024, // 64KB
      ...options
    };
    
    this.generationStats = {
      totalGenerated: 0,
      totalBytes: 0,
      averageSize: 0,
      compressionRatio: 0
    };
  }
  
  generateStaticStream(element, options = {}) {
    const {
      enableCompression = this.options.enableGzip,
      bufferSize = this.options.bufferSize
    } = options;
    
    try {
      // 创建静态渲染流
      const staticStream = renderToStaticNodeStream(element);
      
      // 创建处理流水线
      const pipeline = this.createProcessingPipeline(staticStream, {
        enableCompression,
        bufferSize
      });
      
      return pipeline;
      
    } catch (error) {
      console.error('Failed to create static stream:', error);
      throw error;
    }
  }
  
  createProcessingPipeline(sourceStream, options) {
    const { Transform, PassThrough } = require('stream');
    const zlib = require('zlib');
    
    let originalSize = 0;
    let compressedSize = 0;
    const startTime = Date.now();
    
    // 创建统计流
    const statsStream = new Transform({
      transform(chunk, encoding, callback) {
        originalSize += chunk.length;
        callback(null, chunk);
      }
    });
    
    // 创建缓冲流
    const bufferStream = new Transform({
      highWaterMark: options.bufferSize,
      transform(chunk, encoding, callback) {
        callback(null, chunk);
      }
    });
    
    let pipeline = sourceStream.pipe(statsStream).pipe(bufferStream);
    
    // 可选的压缩流
    if (options.enableCompression) {
      const gzipStream = zlib.createGzip({
        level: this.options.compressionLevel
      });
      
      // 跟踪压缩后大小
      const compressStatsStream = new Transform({
        transform(chunk, encoding, callback) {
          compressedSize += chunk.length;
          callback(null, chunk);
        }
      });
      
      pipeline = pipeline.pipe(gzipStream).pipe(compressStatsStream);
    }
    
    // 完成时更新统计
    pipeline.on('end', () => {
      this.updateStats({
        originalSize,
        compressedSize: options.enableCompression ? compressedSize : originalSize,
        processingTime: Date.now() - startTime,
        compressed: options.enableCompression
      });
    });
    
    return pipeline;
  }
  
  updateStats(streamStats) {
    this.generationStats.totalGenerated++;
    this.generationStats.totalBytes += streamStats.originalSize;
    this.generationStats.averageSize = 
      this.generationStats.totalBytes / this.generationStats.totalGenerated;
    
    if (streamStats.compressed) {
      const ratio = streamStats.compressedSize / streamStats.originalSize;
      this.generationStats.compressionRatio = 
        (this.generationStats.compressionRatio + ratio) / 2;
    }
  }
  
  // 生成静态RSS源
  generateRSSFeed(posts, siteInfo) {
    const RSSComponent = ({ posts, siteInfo }) => (
      <rss version="2.0">
        <channel>
          <title>{siteInfo.title}</title>
          <description>{siteInfo.description}</description>
          <link>{siteInfo.url}</link>
          <language>zh-CN</language>
          <lastBuildDate>{new Date().toUTCString()}</lastBuildDate>
          
          {posts.map(post => (
            <item key={post.id}>
              <title>{post.title}</title>
              <description>{post.summary}</description>
              <link>{`${siteInfo.url}/posts/${post.slug}`}</link>
              <guid>{post.id}</guid>
              <pubDate>{new Date(post.publishedAt).toUTCString()}</pubDate>
              <author>{post.author.email} ({post.author.name})</author>
            </item>
          ))}
        </channel>
      </rss>
    );
    
    return this.generateStaticStream(
      <RSSComponent posts={posts} siteInfo={siteInfo} />,
      { enableCompression: false } // RSS通常不压缩
    );
  }
  
  // 生成站点地图
  generateSitemap(urls) {
    const SitemapComponent = ({ urls }) => (
      <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
        {urls.map((url, index) => (
          <url key={index}>
            <loc>{url.loc}</loc>
            <lastmod>{url.lastmod}</lastmod>
            <changefreq>{url.changefreq || 'weekly'}</changefreq>
            <priority>{url.priority || '0.5'}</priority>
          </url>
        ))}
      </urlset>
    );
    
    return this.generateStaticStream(
      <SitemapComponent urls={urls} />,
      { enableCompression: true }
    );
  }
  
  getStats() {
    return this.generationStats;
  }
}

// 博客网站静态内容生成
function BlogPost({ post, relatedPosts, comments }) {
  return (
    <article className="blog-post">
      <header>
        <h1>{post.title}</h1>
        <div className="post-meta">
          <time dateTime={post.publishedAt}>
            {new Date(post.publishedAt).toLocaleDateString('zh-CN', {
              year: 'numeric',
              month: 'long',
              day: 'numeric'
            })}
          </time>
          <span className="author">作者:{post.author.name}</span>
          <span className="reading-time">阅读时长:{post.readingTime}分钟</span>
        </div>
      </header>
      
      <div className="post-content" dangerouslySetInnerHTML={{ __html: post.content }} />
      
      <section className="post-tags">
        <h3>标签</h3>
        {post.tags.map(tag => (
          <span key={tag.id} className="tag">#{tag.name}</span>
        ))}
      </section>
      
      <section className="related-posts">
        <h3>相关文章</h3>
        <ul>
          {relatedPosts.map(relatedPost => (
            <li key={relatedPost.id}>
              <a href={`/posts/${relatedPost.slug}`}>{relatedPost.title}</a>
              <time>{new Date(relatedPost.publishedAt).toLocaleDateString('zh-CN')}</time>
            </li>
          ))}
        </ul>
      </section>
      
      <section className="comments">
        <h3>评论 ({comments.length})</h3>
        {comments.map(comment => (
          <div key={comment.id} className="comment">
            <div className="comment-header">
              <strong>{comment.author}</strong>
              <time>{new Date(comment.createdAt).toLocaleDateString('zh-CN')}</time>
            </div>
            <div className="comment-content">{comment.content}</div>
          </div>
        ))}
      </section>
    </article>
  );
}

// 使用示例
const staticGenerator = new StaticStreamGenerator({
  enableGzip: true,
  compressionLevel: 9,
  bufferSize: 128 * 1024
});

// Express.js 路由集成
app.get('/posts/:slug', async (req, res) => {
  try {
    const [post, relatedPosts, comments] = await Promise.all([
      getPostBySlug(req.params.slug),
      getRelatedPosts(req.params.slug, 5),
      getPostComments(req.params.slug)
    ]);
    
    if (!post) {
      return res.status(404).send('文章不存在');
    }
    
    const blogElement = (
      <BlogPost 
        post={post} 
        relatedPosts={relatedPosts} 
        comments={comments} 
      />
    );
    
    const stream = staticGenerator.generateStaticStream(blogElement, {
      enableCompression: req.headers['accept-encoding']?.includes('gzip')
    });
    
    // 设置响应头
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    if (req.headers['accept-encoding']?.includes('gzip')) {
      res.setHeader('Content-Encoding', 'gzip');
    }
    res.setHeader('Cache-Control', 'public, max-age=3600'); // 1小时缓存
    
    // 流式响应
    stream.pipe(res);
    
    stream.on('error', (error) => {
      console.error('Static stream error:', error);
      if (!res.headersSent) {
        res.status(500).send('渲染失败');
      }
    });
    
  } catch (error) {
    console.error('Blog post rendering error:', error);
    res.status(500).send('服务器内部错误');
  }
});

// RSS源端点
app.get('/feed.xml', async (req, res) => {
  try {
    const posts = await getRecentPosts(20);
    const siteInfo = {
      title: '我的博客',
      description: '技术分享与思考',
      url: 'https://myblog.com'
    };
    
    const rssStream = staticGenerator.generateRSSFeed(posts, siteInfo);
    
    res.setHeader('Content-Type', 'application/rss+xml; charset=utf-8');
    res.setHeader('Cache-Control', 'public, max-age=1800'); // 30分钟缓存
    
    rssStream.pipe(res);
    
  } catch (error) {
    console.error('RSS generation error:', error);
    res.status(500).send('RSS生成失败');
  }
});

// 站点地图端点
app.get('/sitemap.xml', async (req, res) => {
  try {
    const urls = await generateSitemapUrls();
    const sitemapStream = staticGenerator.generateSitemap(urls);
    
    res.setHeader('Content-Type', 'application/xml; charset=utf-8');
    res.setHeader('Content-Encoding', 'gzip');
    res.setHeader('Cache-Control', 'public, max-age=86400'); // 24小时缓存
    
    sitemapStream.pipe(res);
    
  } catch (error) {
    console.error('Sitemap generation error:', error);
    res.status(500).send('站点地图生成失败');
  }
});

5. renderToPipeableStream (React 18)

用途:React 18的现代流式渲染API,支持Suspense和并发特性。

特点

  • 完整的React 18特性支持
  • 选择性水合
  • Suspense边界流式渲染
  • 更好的错误处理
javascript 复制代码
import { renderToPipeableStream } from 'react-dom/server';
import { Suspense } from 'react';

// React 18 现代流式SSR系统
class ModernStreamingSSR {
  constructor(options = {}) {
    this.options = {
      bootstrapScripts: ['/js/client.js'],
      onShellReady: null,
      onShellError: null,
      onAllReady: null,
      onError: null,
      timeout: 10000,
      ...options
    };
    
    this.renderMetrics = {
      shellReadyTime: 0,
      totalRenderTime: 0,
      suspenseCount: 0,
      errorCount: 0,
      successCount: 0
    };
  }
  
  renderToStream(element, response, renderOptions = {}) {
    const startTime = Date.now();
    let shellReady = false;
    const metrics = { ...this.renderMetrics };
    
    const {
      onShellReady = this.options.onShellReady,
      onShellError = this.options.onShellError,
      onAllReady = this.options.onAllReady,
      onError = this.options.onError,
      bootstrapScripts = this.options.bootstrapScripts,
      timeout = this.options.timeout
    } = renderOptions;
    
    const { pipe, abort } = renderToPipeableStream(element, {
      bootstrapScripts,
      
      // Shell准备就绪 - 包含非Suspense内容
      onShellReady() {
        shellReady = true;
        metrics.shellReadyTime = Date.now() - startTime;
        
        console.log(`Shell ready in ${metrics.shellReadyTime}ms`);
        
        // 设置响应头
        response.statusCode = 200;
        response.setHeader('Content-Type', 'text/html; charset=utf-8');
        response.setHeader('Transfer-Encoding', 'chunked');
        response.setHeader('X-Shell-Time', metrics.shellReadyTime);
        
        // 开始流式传输
        pipe(response);
        
        // 调用自定义回调
        onShellReady?.();
      },
      
      // Shell渲染错误
      onShellError(error) {
        console.error('Shell render error:', error);
        metrics.errorCount++;
        
        response.statusCode = 500;
        response.setHeader('Content-Type', 'text/html; charset=utf-8');
        response.end(this.getErrorHTML(error, 'shell'));
        
        onShellError?.(error);
      },
      
      // 所有内容准备就绪 - 包括Suspense内容
      onAllReady() {
        metrics.totalRenderTime = Date.now() - startTime;
        metrics.successCount++;
        
        console.log(`All content ready in ${metrics.totalRenderTime}ms`);
        
        // 如果shell还未准备好,现在开始传输
        if (!shellReady) {
          response.statusCode = 200;
          response.setHeader('Content-Type', 'text/html; charset=utf-8');
          pipe(response);
        }
        
        // 更新响应头
        response.setHeader('X-Total-Time', metrics.totalRenderTime);
        
        onAllReady?.(metrics);
      },
      
      // 渲染过程中的错误
      onError(error, errorInfo) {
        console.error('Render error:', error, errorInfo);
        metrics.errorCount++;
        
        // 记录错误详情
        this.logRenderError(error, errorInfo);
        
        onError?.(error, errorInfo);
      }
    });
    
    // 设置超时处理
    const timeoutId = setTimeout(() => {
      console.warn(`Render timeout after ${timeout}ms, aborting...`);
      abort();
    }, timeout);
    
    // 清理资源
    response.on('close', () => {
      clearTimeout(timeoutId);
      console.log('Client disconnected, cleaning up render stream');
    });
    
    response.on('finish', () => {
      clearTimeout(timeoutId);
      this.updateGlobalMetrics(metrics);
    });
    
    return { abort, metrics };
  }
  
  logRenderError(error, errorInfo) {
    const errorLog = {
      message: error.message,
      stack: error.stack,
      componentStack: errorInfo?.componentStack,
      timestamp: new Date().toISOString(),
      renderContext: 'streaming'
    };
    
    // 这里可以发送到错误监控服务
    console.error('Detailed render error:', errorLog);
  }
  
  getErrorHTML(error, context) {
    return `
      <!DOCTYPE html>
      <html lang="zh-CN">
        <head>
          <meta charset="utf-8">
          <title>页面加载失败</title>
          <style>
            body { font-family: system-ui, sans-serif; margin: 40px; }
            .error { background: #fee; border: 1px solid #fcc; padding: 20px; border-radius: 4px; }
            .error-title { color: #c33; margin: 0 0 10px 0; }
            .error-message { color: #666; }
            .retry-btn { 
              background: #007bff; color: white; border: none; 
              padding: 10px 20px; border-radius: 4px; cursor: pointer; margin-top: 15px;
            }
          </style>
        </head>
        <body>
          <div class="error">
            <h1 class="error-title">页面渲染失败</h1>
            <p class="error-message">
              渲染${context === 'shell' ? '基础内容' : '页面内容'}时发生错误,请稍后重试。
            </p>
            <button class="retry-btn" onclick="window.location.reload()">重新加载</button>
          </div>
          <script>
            console.error('SSR Error Context:', '${context}');
            console.error('Error Details:', ${JSON.stringify(error.message)});
            
            // 5秒后自动重新加载
            setTimeout(() => {
              window.location.reload();
            }, 5000);
          </script>
        </body>
      </html>
    `;
  }
  
  updateGlobalMetrics(metrics) {
    Object.keys(this.renderMetrics).forEach(key => {
      if (typeof this.renderMetrics[key] === 'number' && typeof metrics[key] === 'number') {
        this.renderMetrics[key] = (this.renderMetrics[key] + metrics[key]) / 2;
      }
    });
  }
  
  getStats() {
    return {
      ...this.renderMetrics,
      successRate: this.renderMetrics.successCount / 
        (this.renderMetrics.successCount + this.renderMetrics.errorCount)
    };
  }
}

// 支持Suspense的现代应用组件
function ModernApp({ userId, initialData }) {
  return (
    <html lang="zh-CN">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>现代SSR应用</title>
        <link rel="stylesheet" href="/css/app.css" />
      </head>
      <body>
        <div id="root">
          {/* 立即可见的内容 - Shell */}
          <Header />
          <Navigation />
          
          <main>
            {/* 快速加载的内容 */}
            <section className="hero">
              <h1>欢迎使用现代SSR应用</h1>
              <p>基于React 18的流式服务端渲染</p>
            </section>
            
            {/* 需要数据获取的内容 - Suspense边界 */}
            <Suspense fallback={<UserProfileSkeleton />}>
              <UserProfile userId={userId} />
            </Suspense>
            
            <Suspense fallback={<DashboardSkeleton />}>
              <UserDashboard userId={userId} />
            </Suspense>
            
            <Suspense fallback={<RecommendationsSkeleton />}>
              <PersonalizedRecommendations userId={userId} />
            </Suspense>
            
            {/* 低优先级内容 */}
            <Suspense fallback={<FooterSkeleton />}>
              <DynamicFooter />
            </Suspense>
          </main>
        </div>
        
        {/* 性能监控脚本 */}
        <script dangerouslySetInnerHTML={{
          __html: `
            window.__SSR_METRICS__ = {
              renderStart: ${Date.now()},
              shellReady: false,
              allReady: false
            };
            
            // 标记shell就绪
            if (document.readyState === 'loading') {
              document.addEventListener('DOMContentLoaded', () => {
                window.__SSR_METRICS__.shellReady = true;
                console.log('Shell hydration ready');
              });
            } else {
              window.__SSR_METRICS__.shellReady = true;
            }
          `
        }} />
      </body>
    </html>
  );
}

// 异步数据组件示例
async function UserProfile({ userId }) {
  // 模拟异步数据获取
  const user = await new Promise(resolve => {
    setTimeout(() => {
      resolve({
        id: userId,
        name: '张三',
        avatar: '/avatars/zhangsan.jpg',
        bio: '全栈开发工程师,专注于React和Node.js开发',
        joinDate: '2020-01-15',
        followers: 1234,
        following: 567
      });
    }, Math.random() * 1000 + 500); // 0.5-1.5秒随机延迟
  });
  
  return (
    <section className="user-profile">
      <div className="profile-header">
        <img src={user.avatar} alt={user.name} className="avatar" />
        <div className="profile-info">
          <h2>{user.name}</h2>
          <p>{user.bio}</p>
          <div className="profile-stats">
            <span>{user.followers} 关注者</span>
            <span>{user.following} 正在关注</span>
            <span>加入于 {new Date(user.joinDate).toLocaleDateString('zh-CN')}</span>
          </div>
        </div>
      </div>
    </section>
  );
}

async function UserDashboard({ userId }) {
  const dashboardData = await new Promise(resolve => {
    setTimeout(() => {
      resolve({
        totalPosts: 42,
        totalLikes: 1337,
        totalComments: 89,
        recentActivity: [
          { type: 'post', title: '发布了新文章:React 18 新特性解析', time: '2小时前' },
          { type: 'like', title: '点赞了文章:JavaScript 性能优化', time: '5小时前' },
          { type: 'comment', title: '评论了文章:现代前端架构设计', time: '1天前' }
        ]
      });
    }, Math.random() * 800 + 300);
  });
  
  return (
    <section className="user-dashboard">
      <h3>个人统计</h3>
      <div className="stats-grid">
        <div className="stat-card">
          <h4>发布文章</h4>
          <span className="stat-number">{dashboardData.totalPosts}</span>
        </div>
        <div className="stat-card">
          <h4>获得点赞</h4>
          <span className="stat-number">{dashboardData.totalLikes}</span>
        </div>
        <div className="stat-card">
          <h4>收到评论</h4>
          <span className="stat-number">{dashboardData.totalComments}</span>
        </div>
      </div>
      
      <div className="recent-activity">
        <h4>最近活动</h4>
        <ul>
          {dashboardData.recentActivity.map((activity, index) => (
            <li key={index} className={`activity-${activity.type}`}>
              <span className="activity-title">{activity.title}</span>
              <span className="activity-time">{activity.time}</span>
            </li>
          ))}
        </ul>
      </div>
    </section>
  );
}

// 骨架屏组件
function UserProfileSkeleton() {
  return (
    <section className="user-profile skeleton">
      <div className="profile-header">
        <div className="avatar skeleton-box"></div>
        <div className="profile-info">
          <div className="skeleton-text skeleton-title"></div>
          <div className="skeleton-text"></div>
          <div className="skeleton-text skeleton-half"></div>
        </div>
      </div>
    </section>
  );
}

function DashboardSkeleton() {
  return (
    <section className="user-dashboard skeleton">
      <div className="skeleton-text skeleton-title"></div>
      <div className="stats-grid">
        {[1, 2, 3].map(i => (
          <div key={i} className="stat-card skeleton-box"></div>
        ))}
      </div>
    </section>
  );
}

// Express.js 集成
const modernSSR = new ModernStreamingSSR({
  bootstrapScripts: ['/js/client.js', '/js/polyfills.js'],
  timeout: 15000
});

app.get('/app/:userId?', (req, res) => {
  const userId = req.params.userId || 'guest';
  
  const appElement = <ModernApp userId={userId} />;
  
  const { abort } = modernSSR.renderToStream(appElement, res, {
    onShellReady: () => {
      console.log(`Shell ready for user: ${userId}`);
    },
    onAllReady: (metrics) => {
      console.log(`Complete render for user: ${userId}`, metrics);
    },
    onError: (error, errorInfo) => {
      console.error(`Render error for user: ${userId}`, error);
      // 可以在这里发送错误监控
    }
  });
  
  // 请求取消处理
  req.on('close', () => {
    console.log(`Client disconnected for user: ${userId}`);
    abort();
  });
});

// 性能统计端点
app.get('/api/ssr-stats', (req, res) => {
  const stats = modernSSR.getStats();
  res.json({
    status: 'ok',
    ssr: stats,
    timestamp: new Date().toISOString()
  });
});

6. renderToReadableStream (Web Streams)

用途:为Web环境(如Cloudflare Workers、Deno)设计的流式渲染API。

特点

  • Web标准流API
  • 边缘计算友好
  • 现代浏览器原生支持
  • 更好的流控制
javascript 复制代码
// 注意:这个API主要用于Web环境,如Cloudflare Workers
// 这里提供Node.js环境的模拟实现示例

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

// Web流式SSR服务(适用于边缘计算环境)
class WebStreamingSSR {
  constructor(options = {}) {
    this.options = {
      signal: null,
      onError: null,
      bootstrapScripts: [],
      ...options
    };
    
    this.streamMetrics = {
      totalStreams: 0,
      activeStreams: 0,
      averageSize: 0,
      errorRate: 0
    };
  }
  
  async renderToWebStream(element, options = {}) {
    const startTime = Date.now();
    let bytesGenerated = 0;
    
    const {
      signal = this.options.signal,
      onError = this.options.onError,
      bootstrapScripts = this.options.bootstrapScripts
    } = options;
    
    try {
      this.streamMetrics.totalStreams++;
      this.streamMetrics.activeStreams++;
      
      // 创建Web可读流
      const stream = await renderToReadableStream(element, {
        bootstrapScripts,
        signal,
        onError: (error, errorInfo) => {
          console.error('Web stream render error:', error);
          this.streamMetrics.errorRate = 
            (this.streamMetrics.errorRate + 1) / this.streamMetrics.totalStreams;
          onError?.(error, errorInfo);
        }
      });
      
      // 创建增强的流处理器
      const enhancedStream = this.createEnhancedWebStream(stream, {
        onData: (chunk) => {
          bytesGenerated += chunk.length;
        },
        onComplete: () => {
          const duration = Date.now() - startTime;
          this.updateStreamMetrics(bytesGenerated, duration);
          this.streamMetrics.activeStreams--;
        }
      });
      
      return enhancedStream;
      
    } catch (error) {
      this.streamMetrics.activeStreams--;
      console.error('Failed to create web stream:', error);
      throw error;
    }
  }
  
  createEnhancedWebStream(originalStream, callbacks) {
    const { onData, onComplete } = callbacks;
    
    // 创建转换流来监控数据
    const transformStream = new TransformStream({
      transform(chunk, controller) {
        onData?.(chunk);
        controller.enqueue(chunk);
      },
      flush() {
        onComplete?.();
      }
    });
    
    return originalStream.pipeThrough(transformStream);
  }
  
  updateStreamMetrics(bytes, duration) {
    const currentAvg = this.streamMetrics.averageSize;
    const count = this.streamMetrics.totalStreams;
    this.streamMetrics.averageSize = (currentAvg * (count - 1) + bytes) / count;
  }
  
  getMetrics() {
    return this.streamMetrics;
  }
}

// Cloudflare Workers 示例
const webSSR = new WebStreamingSSR();

// Workers 环境的应用组件
function WorkersApp({ url, userAgent, cf }) {
  return (
    <html lang="zh-CN">
      <head>
        <meta charSet="utf-8" />
        <title>边缘计算SSR应用</title>
        <meta name="viewport" content="width=device-width, initial-scale=1" />
      </head>
      <body>
        <div id="root">
          <header>
            <h1>边缘计算渲染</h1>
            <p>此页面在离您最近的边缘节点渲染</p>
          </header>
          
          <main>
            <section className="request-info">
              <h2>请求信息</h2>
              <dl>
                <dt>访问URL:</dt>
                <dd>{url}</dd>
                
                <dt>用户代理:</dt>
                <dd>{userAgent}</dd>
                
                {cf && (
                  <>
                    <dt>数据中心:</dt>
                    <dd>{cf.colo}</dd>
                    
                    <dt>国家/地区:</dt>
                    <dd>{cf.country}</dd>
                    
                    <dt>时区:</dt>
                    <dd>{cf.timezone}</dd>
                  </>
                )}
              </dl>
            </section>
            
            <section className="edge-features">
              <h2>边缘计算特性</h2>
              <ul>
                <li>✅ 超低延迟渲染</li>
                <li>✅ 全球CDN分发</li>
                <li>✅ 自动扩缩容</li>
                <li>✅ 零冷启动</li>
              </ul>
            </section>
          </main>
          
          <footer>
            <p>渲染时间: {new Date().toISOString()}</p>
            <p>Powered by Cloudflare Workers + React SSR</p>
          </footer>
        </div>
      </body>
    </html>
  );
}

// Cloudflare Workers 处理器
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    
    // 路由处理
    if (url.pathname === '/') {
      try {
        const stream = await webSSR.renderToWebStream(
          <WorkersApp 
            url={request.url}
            userAgent={request.headers.get('User-Agent')}
            cf={request.cf}
          />,
          {
            // 处理取消信号
            signal: request.signal,
            onError: (error) => {
              console.error('Workers SSR error:', error);
            }
          }
        );
        
        return new Response(stream, {
          headers: {
            'Content-Type': 'text/html; charset=utf-8',
            'Cache-Control': 'public, max-age=300', // 5分钟缓存
            'X-Rendered-By': 'Cloudflare-Workers-SSR'
          }
        });
        
      } catch (error) {
        console.error('Workers render failed:', error);
        
        return new Response(
          `<h1>渲染失败</h1><p>错误信息: ${error.message}</p>`,
          {
            status: 500,
            headers: { 'Content-Type': 'text/html; charset=utf-8' }
          }
        );
      }
    }
    
    // API路由示例
    if (url.pathname === '/api/metrics') {
      const metrics = webSSR.getMetrics();
      return Response.json({
        metrics,
        worker: {
          colo: request.cf?.colo,
          country: request.cf?.country,
          timestamp: new Date().toISOString()
        }
      });
    }
    
    return new Response('Not Found', { status: 404 });
  }
};

// Deno 环境示例
if (typeof Deno !== 'undefined') {
  const server = Deno.serve({ port: 8000 }, async (request) => {
    const url = new URL(request.url);
    
    if (url.pathname === '/') {
      try {
        const stream = await webSSR.renderToWebStream(
          <WorkersApp 
            url={request.url}
            userAgent={request.headers.get('User-Agent')}
          />
        );
        
        return new Response(stream, {
          headers: {
            'Content-Type': 'text/html; charset=utf-8',
            'X-Rendered-By': 'Deno-SSR'
          }
        });
        
      } catch (error) {
        return new Response(`Error: ${error.message}`, { status: 500 });
      }
    }
    
    return new Response('Not Found', { status: 404 });
  });
  
  console.log('Deno SSR server running on http://localhost:8000');
}

// Node.js环境的Web Streams适配器
class NodeWebStreamAdapter {
  static async renderToNodeResponse(element, nodeResponse, options = {}) {
    try {
      // 在Node.js中模拟Web Streams
      const webStream = await renderToReadableStream(element, options);
      
      // 转换为Node.js流
      const nodeStream = this.webStreamToNodeStream(webStream);
      
      // 设置响应头
      nodeResponse.setHeader('Content-Type', 'text/html; charset=utf-8');
      nodeResponse.setHeader('X-Stream-Type', 'web-streams-adapter');
      
      // 管道到响应
      nodeStream.pipe(nodeResponse);
      
      return nodeStream;
      
    } catch (error) {
      console.error('Web stream adapter error:', error);
      nodeResponse.status(500).send('渲染失败');
      throw error;
    }
  }
  
  static webStreamToNodeStream(webStream) {
    const { Readable } = require('stream');
    const reader = webStream.getReader();
    
    return new Readable({
      async read() {
        try {
          const { done, value } = await reader.read();
          
          if (done) {
            this.push(null); // 结束流
          } else {
            this.push(value);
          }
        } catch (error) {
          this.destroy(error);
        }
      }
    });
  }
}

// Node.js Express 使用示例
if (typeof require !== 'undefined') {
  const express = require('express');
  const app = express();
  
  app.get('/web-streams', async (req, res) => {
    const element = <WorkersApp url={req.url} userAgent={req.get('User-Agent')} />;
    
    try {
      await NodeWebStreamAdapter.renderToNodeResponse(element, res, {
        onError: (error) => {
          console.error('Node.js web streams error:', error);
        }
      });
    } catch (error) {
      if (!res.headersSent) {
        res.status(500).send('渲染失败');
      }
    }
  });
  
  // 性能对比端点
  app.get('/api/stream-comparison', async (req, res) => {
    const metrics = webSSR.getMetrics();
    
    res.json({
      webStreams: metrics,
      nodeJs: {
        platform: process.platform,
        version: process.version,
        memory: process.memoryUsage()
      },
      comparison: {
        advantages: [
          'Web标准兼容',
          '边缘计算友好',
          '更好的流控制',
          '现代浏览器原生支持'
        ],
        limitations: [
          'Node.js环境需要适配',
          '生态系统相对较新',
          '某些工具库可能不兼容'
        ]
      }
    });
  });
  
  const port = process.env.PORT || 3000;
  app.listen(port, () => {
    console.log(`Node.js server with Web Streams running on port ${port}`);
  });
}

React 18 新特性深度应用

Concurrent Features 在 SSR 中的应用

React 18 引入的并发特性为 SSR 带来了革命性的性能提升,特别是在处理大型应用和复杂数据获取场景中。

javascript 复制代码
import { Suspense, startTransition, useDeferredValue, useTransition } from 'react';
import { renderToPipeableStream } from 'react-dom/server';

// 并发SSR应用架构
class ConcurrentSSRApp {
  constructor() {
    this.suspenseTracker = new Map();
    this.renderPriorities = new Map();
  }
  
  // 优先级渲染控制
  renderWithPriority(element, priority = 'normal') {
    const priorityMap = {
      urgent: 1,
      normal: 2,
      background: 3
    };
    
    return new Promise((resolve, reject) => {
      const priorityValue = priorityMap[priority] || 2;
      
      // 使用优先级队列管理渲染任务
      this.scheduleRender(element, priorityValue, resolve, reject);
    });
  }
  
  scheduleRender(element, priority, resolve, reject) {
    // 模拟React的并发调度
    const renderTask = () => {
      try {
        const stream = renderToPipeableStream(element, {
          onShellReady() {
            console.log(`Priority ${priority} render shell ready`);
          },
          onAllReady() {
            console.log(`Priority ${priority} render complete`);
            resolve(stream);
          },
          onError(error) {
            console.error(`Priority ${priority} render error:`, error);
            reject(error);
          }
        });
      } catch (error) {
        reject(error);
      }
    };
    
    // 根据优先级调度任务
    if (priority === 1) {
      // 高优先级立即执行
      renderTask();
    } else if (priority === 2) {
      // 正常优先级在下一个事件循环执行
      setImmediate(renderTask);
    } else {
      // 低优先级延迟执行
      setTimeout(renderTask, 10);
    }
  }
}

// 支持并发特性的组件示例
function ConcurrentApp({ userId, filters, preferences }) {
  return (
    <html>
      <head>
        <title>并发SSR应用</title>
      </head>
      <body>
        <div id="root">
          {/* 高优先级:立即显示的内容 */}
          <Header />
          <Navigation />
          
          {/* 中优先级:主要内容区域 */}
          <main>
            <Suspense fallback={<MainContentSkeleton />}>
              <MainContent userId={userId} />
            </Suspense>
            
            {/* 低优先级:次要内容 */}
            <aside>
              <Suspense fallback={<SidebarSkeleton />}>
                <Sidebar preferences={preferences} />
              </Suspense>
            </aside>
          </main>
          
          {/* 最低优先级:底部内容 */}
          <Suspense fallback={<FooterSkeleton />}>
            <DynamicFooter />
          </Suspense>
        </div>
      </body>
    </html>
  );
}

// 智能数据预取组件
function SmartDataFetcher({ children, fallback, priority = 'normal' }) {
  const [isPending, startTransition] = useTransition();
  
  // 在服务端,直接渲染子组件
  if (typeof window === 'undefined') {
    return children;
  }
  
  // 在客户端,使用transition管理更新
  return (
    <Suspense fallback={fallback}>
      {isPending ? fallback : children}
    </Suspense>
  );
}

// 并发数据获取Hook
function useConcurrentData(fetcher, deps = []) {
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [isPending, startTransition] = useTransition();
  
  useEffect(() => {
    let cancelled = false;
    
    const fetchData = async () => {
      try {
        setIsLoading(true);
        setError(null);
        
        const result = await fetcher();
        
        if (!cancelled) {
          // 使用transition来更新状态,避免阻塞UI
          startTransition(() => {
            setData(result);
            setIsLoading(false);
          });
        }
      } catch (err) {
        if (!cancelled) {
          setError(err);
          setIsLoading(false);
        }
      }
    };
    
    fetchData();
    
    return () => {
      cancelled = true;
    };
  }, deps);
  
  return { data, error, isLoading, isPending };
}

// 使用示例
const concurrentSSR = new ConcurrentSSRApp();

// Express路由with concurrent features
app.get('/concurrent/:userId', async (req, res) => {
  const { userId } = req.params;
  const { priority = 'normal' } = req.query;
  
  try {
    const appElement = (
      <ConcurrentApp 
        userId={userId}
        filters={req.query.filters}
        preferences={req.query.preferences}
      />
    );
    
    // 根据请求优先级进行渲染
    const stream = await concurrentSSR.renderWithPriority(appElement, priority);
    
    res.setHeader('Content-Type', 'text/html; charset=utf-8');
    res.setHeader('X-Render-Priority', priority);
    
    stream.pipe(res);
    
  } catch (error) {
    console.error('Concurrent SSR error:', error);
    res.status(500).send('并发渲染失败');
  }
});

总结

React 服务端渲染的 Server API 为构建高性能 Web 应用提供了强大的工具集。通过本文的深入解析,我们全面覆盖了所有 renderToXX 方法:

核心 API 总结

  1. renderToString - 传统同步渲染,适用于简单应用
  2. renderToStaticMarkup - 纯静态内容生成,体积更小
  3. renderToNodeStream - Node.js流式渲染,支持背压处理
  4. renderToStaticNodeStream - 静态内容流,无React属性
  5. renderToPipeableStream - React 18现代流式API,完整并发支持
  6. renderToReadableStream - Web标准流,边缘计算友好

技术选择指南

  • 传统应用 : 使用 renderToString 进行快速迁移
  • 静态站点 : 使用 renderToStaticMarkup 获得最小输出
  • 大型应用 : 使用 renderToPipeableStream 获得最佳性能
  • 边缘计算 : 使用 renderToReadableStream 实现全球分发

最佳实践

  1. 渐进式升级: 从传统API逐步迁移到React 18新特性
  2. 合理选择: 根据应用场景选择最适合的渲染方法
  3. 性能监控: 建立完整的渲染性能监控体系
  4. 错误处理: 实现完善的错误处理和降级机制

通过掌握这些 Server API,开发者能够构建出高性能、可扩展的服务端渲染应用,为用户提供优秀的访问体验。

相关推荐
2301_781668615 小时前
前端基础 JS Vue3 Ajax
前端
上单带刀不带妹5 小时前
前端安全问题怎么解决
前端·安全
Fly-ping5 小时前
【前端】JavaScript 的事件循环 (Event Loop)
开发语言·前端·javascript
SunTecTec6 小时前
IDEA 类上方注释 签名
服务器·前端·intellij-idea
在逃的吗喽6 小时前
黑马头条项目详解
前端·javascript·ajax
袁煦丞6 小时前
有Nextcloud家庭共享不求人:cpolar内网穿透实验室第471个成功挑战
前端·程序员·远程工作
小磊哥er7 小时前
【前端工程化】前端项目开发过程中如何做好通知管理?
前端
拾光拾趣录7 小时前
一次“秒开”变成“转菊花”的线上事故
前端
你我约定有三7 小时前
前端笔记:同源策略、跨域问题
前端·笔记
JHCan3337 小时前
一个没有手动加分号引发的bug
前端·javascript·bug