SSR不是HTML本身实现的,而是服务器(如Node.js)在响应请求时动态生成含真实内容的HTML字符串并返回;静态HTML文件无法实现SSR,因其缺乏服务端数据注入、路由匹配与组件渲染能力。

HTML 本身不“实现”SSR——SSR 是一种服务端行为,不是 HTML 标签或属性能开启的功能。真正起作用的是服务器(如 Node.js、Spring Boot、PHP)在响应 HTTP 请求时,动态生成包含真实内容的 <html> 字符串并返回给浏览器。
SSR 的 HTML 是怎么来的?
它不是手写的静态文件,而是由服务端程序实时拼出来的完整字符串。比如 Express 中一个最简 SSR 响应:
app.get('/user/:id', (req, res) => {
const userId = req.params.id;
const userData = { name: 'Alice', role: 'admin' }; // 模拟数据获取
const html = `<!DOCTYPE html>
<html>
<head><title>User ${userId}</title></head>
<body>
<h1>${userData.name}</h1>
<p>Role: ${userData.role}</p>
</body>
</html>`;
res.send(html);
});
这个 res.send(html) 返回的就是一份真正的 SSR HTML:内容已填充、无需 JS 渲染即可显示。
- 关键点:HTML 字符串里所有
${...}插值都已在服务端执行完毕,浏览器收到的就是最终结果 - 如果这里写成
<h1 id="name"></h1>再靠 JS 去填,那就退化成 CSR 了 - 模板引擎(如 EJS、Thymeleaf)只是让这种拼接更安全、可维护,但本质仍是服务端输出完整 HTML
为什么不能只靠 HTML 文件实现 SSR?
静态 index.html 文件无论放多少 JS/CSS,只要没经服务端动态处理,就不是 SSR。常见误解包括:
立即学习“前端免费学习笔记(深入)”;
- 把
<script type="module">或defer当作 SSR——这只是加载优化,页面仍是空壳 - 用
fetch()在<script>里拉数据再innerHTML填充——这是典型的 CSR,搜索引擎看不到内容 - 认为加了
<meta name="description">就算 SEO 友好——爬虫不执行 JS,只看初始 HTML 文本
真正 SSR 的 HTML 必须在响应体中就含业务数据,例如商品标题、文章正文、用户昵称——这些字段不能是占位符,也不能靠客户端补全。
SSR 中 HTML 的关键结构要求
服务端生成的 HTML 不是随意拼的,要为后续客户端接管(hydration)留出契约:
- 必须有唯一根容器,如
<div id="root"></div>,且客户端 JS 必须挂载到同一节点 - 若使用状态同步(如 React),需将初始数据序列化进 HTML,例如:
<script>window.__INITIAL_STATE__ = {"user":{"id":123}};</script> - 避免服务端和客户端渲染结果不一致(hydration error),比如服务端输出
<time>2026-04-30</time>,客户端却用new Date().toDateString()渲染成英文格式——这会触发 React 警告甚至降级为 CSR
最容易被忽略的是:服务端生成的 DOM 结构、class 名、文本内容,必须和客户端首次 render 完全一致。哪怕多一个空格、少一个属性,都可能破坏 hydration 流程。



















