不能直接用 res.send(htmlstring) 进行 ssr,因为必须确保服务端 html 包含初始状态、dom 结构严格对齐、无多余标签、避免 xss,且所有交互区块需统一 ssr 或显式 hydrate,否则导致静默 hydration 失败。

为什么不能直接用 res.send(htmlString) 吐模板
服务端渲染不是“拼个字符串发过去”就完事。浏览器收到 HTML 后要执行 hydration,而 res.send(htmlString) 若没包裹初始状态、没对齐 DOM 结构、没处理 XSS 边界,客户端 React/Vue 就会丢光标、错 history、失焦点——这不是 JS 报错,是 hydration 失败的静默崩坏。
必须确保服务端生成的 HTML 字符串:包含 window.__INITIAL_STATE__(≤5KB)、<div id="root">${ssrHtml}</div> 闭合完整、无多余 或 标签(否则插入时会破坏 DOM 树)。
- 错误示例:
res.send('<div id="root">...</div>')→ 客户端 mount 时 DOM 结构不匹配 - 正确做法:服务端只返回纯 fragment 字符串,由模板引擎注入到预设 layout 中
- 若用 EJS/Nunjucks,模板里写
而非,避免 HTML 转义破坏结构
renderToString 必须在服务端调用,且不能复用客户端组件逻辑
React 的 renderToString 是 Node 环境专用 API,它不触发任何浏览器生命周期(如 useEffect、useLayoutEffect),也不访问 window 或 document。一旦你在组件里写了 useEffect(() => fetch(...), []),服务端就静默跳过——结果就是首屏没数据、高亮没内容、编辑器空着等用户粘贴。
真实场景中,HTML 在线运行工具需要预加载默认代码、预执行简单 JS 片段、生成 AST 高亮结构——这些都得在服务端手动做,而不是靠客户端“补上”。
- 预执行必须用沙箱(如
vm2或isolated-vm),不能直接eval - 语法高亮不能依赖客户端 Prism.js 初始化,得在服务端生成带
class="token keyword"的 HTML 字符串 - 语言选择器的默认值、是否已执行等状态,必须提前塞进
props传给renderToString
用 <template></template> 注入动态片段时,hydration 边界容易被忽略
如果你的服务端已输出主框架(SSR 页面),又用 fetch 拉取一个 HTML 片段再通过 <template></template> 插入,那两个 DOM 区域的 hydration 是割裂的:服务端吐出的节点能 hydrate,但 <template></template> 克隆出来的部分不会自动绑定事件、不会恢复 React 状态、甚至 ID 冲突会导致 querySelector 找错元素。
关键点在于:所有需交互的动态区块,要么全 SSR,要么全客户端接管;混用时必须显式调用 ReactDOM.hydrateRoot 或 createRoot 绑定新挂载点,且确保该子树的 props 与服务端一致。
- 错误操作:
template.content直接appendChild到已 hydrate 的容器内 → 事件丢失、状态不同步 - 正确路径:克隆后挂载到独立容器(如
<div id="dynamic-panel"></div>),再用hydrateRoot单独接管 - 服务端返回的片段里不能含
<script></script>或内联事件(如onclick),它们会被忽略,且破坏 hydration 一致性
html-minifier 压缩 SSR 输出时,ignoreCustomFragments 必须配准模板语法
SSR 输出的 HTML 往往夹杂模板标记(如 EJS 的 、Nunjucks 的 <code>{%、Handlebars 的 {{),如果 html-minifier 不识别这些,就会把 压成 ,导致服务端解析失败或 XSS 漏洞。
默认配置只保 和 <code>,其他模板引擎必须手动扩展 ignoreCustomFragments。
- EJS 用户加:
// - Handlebars 用户加:
/{{[\s\S]*?}}/和/{#[\s\S]*?#}/ - Nunjucks 用户加:
/{%[\s\S]*?%}/ - 切记:正则要覆盖换行(
[\s\S]),否则多行模板会被截断压缩
服务端模板引擎的性能瓶颈不在渲染本身,而在 hydration 一致性校验和动态片段边界管理——这两处出错,页面看起来正常,但编辑、执行、切换语言时会随机失焦或状态错乱,很难 debug。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











