如何在.NET 7.0中为已编译的React应用实现SSR/预渲染?
.NET 7 结合已编译React应用实现SSR/预渲染的最小成本方案
优先考虑预渲染(最低成本)
预渲染适合不需要动态内容的场景,直接在部署或启动时生成静态HTML,部署后甚至可以移除Node环境,完全满足最小成本评估需求。
步骤1:编写React预渲染脚本
基于你已有的、支持hydrate的编译后代码,写一个极简Node脚本(比如prerender.js),用ReactDOMServer.renderToString生成页面HTML:
const React = require('react'); const ReactDOMServer = require('react-dom/server'); // 替换成你实际编译后的入口组件路径 const App = require('./build/static/js/main.[hash].js').App; // 支持传入路由参数,生成对应页面 const route = process.argv[2] || '/'; const appHtml = ReactDOMServer.renderToString(<App location={route} />); // 输出完整HTML框架,保留客户端hydrate入口 console.log(` <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="/static/css/main.[hash].css"> <!-- 替换成你的编译后CSS路径 --> </head> <body> <div id="root">${appHtml}</div> <script src="/static/js/main.[hash].js"></script> <!-- 客户端hydrate脚本 --> </body> </html> `);
提示:如果你的编译后代码是ES模块格式,需要将脚本改为ESM规范,或用
node --experimental-modules prerender.js运行。
步骤2:.NET端集成预渲染
- 静态部署模式:部署时运行脚本生成对应路由的HTML文件,直接用.NET静态文件中间件托管;
- 启动时生成模式:在.NET启动阶段调用脚本生成HTML并缓存,后续请求直接返回缓存内容:
using System.Diagnostics; var builder = WebApplication.CreateBuilder(args); // 预渲染首页HTML并注入DI容器 builder.Services.AddSingleton<string>(sp => { var process = new Process(); process.StartInfo.FileName = "node"; process.StartInfo.Arguments = "prerender.js /"; process.StartInfo.RedirectStandardOutput = true; process.Start(); var html = process.StandardOutput.ReadToEnd(); process.WaitForExit(); return html; }); var app = builder.Build(); // 返回预渲染的首页 app.MapGet("/", (string prerenderedHtml) => Results.Content(prerenderedHtml, "text/html")); // 托管React编译后的静态资源 app.UseStaticFiles(); app.Run();
轻量SSR方案(动态渲染)
如果需要根据请求动态生成页面(比如带用户上下文的路由),可以用以下无依赖方案,避开过时的SpaServices包:
步骤1:搭建Node SSR服务
写一个极简Express服务,处理.NET的渲染请求,返回React渲染后的HTML片段:
const express = require('express'); const React = require('react'); const ReactDOMServer = require('react-dom/server'); const App = require('./build/static/js/main.[hash].js').App; const app = express(); app.use(express.json()); // 接收路由和自定义props,返回渲染后的HTML app.post('/render', (req, res) => { const { route, props } = req.body; const appHtml = ReactDOMServer.renderToString(<App location={route} {...props} />); res.send(appHtml); }); app.listen(3001, () => console.log('SSR服务运行在3001端口'));
步骤2:.NET中间件调用SSR服务
在.NET中添加中间件,每次请求时调用Node SSR服务,获取渲染后的HTML并注入模板:
using System.Net.Http.Json; var builder = WebApplication.CreateBuilder(args); // 注册HTTP客户端用于调用SSR服务 builder.Services.AddHttpClient("SSRClient", client => { client.BaseAddress = new Uri("http://localhost:3001/"); }); var app = builder.Build(); app.Use(async (context, next) => { // 拦截需要SSR的路由(这里以根路径为例,可根据需求调整) if (context.Request.Path.StartsWithSegments("/")) { var httpClient = context.RequestServices.GetRequiredService<IHttpClientFactory>().CreateClient("SSRClient"); var route = context.Request.Path.ToString(); // 调用SSR服务获取渲染后的HTML var renderResult = await httpClient.PostAsJsonAsync("/render", new { route }); var appHtml = await renderResult.Content.ReadAsStringAsync(); // 构造完整HTML页面 var fullHtml = $@" <!DOCTYPE html> <html> <head> <link rel='stylesheet' href='/static/css/main.[hash].css'> </head> <body> <div id='root'>{appHtml}</div> <script src='/static/js/main.[hash].js'></script> </body> </html>"; await context.Response.WriteAsync(fullHtml); return; } await next(); }); // 托管React静态资源 app.UseStaticFiles(); app.Run();
关键注意事项
- 无需修改现有React编译代码,只需确保客户端入口用
ReactDOM.hydrate替代ReactDOM.render; - 预渲染场景下,部署完成后可移除Node环境,进一步降低依赖;
- 完全避开
Microsoft.AspNetCore.SpaServices.Extensions,该包已停止维护,文档缺失问题无法解决。
内容的提问来源于stack exchange,提问作者Leonard
相关产品推荐
相关产品推荐

