服务端渲染的html必须与客户端首次挂载的虚拟dom完全一致,否则水合失败;根本原因是状态不可序列化、rendertostring与hydrateroot参数不匹配、initial state注入时机错误等导致dom比对失效。

服务端渲染的 HTML 必须与客户端首次挂载时的虚拟 DOM 完全一致,否则水合会失败——这不是警告,是硬性约束。
为什么 innerHTML 在 SSR 中容易触发重复渲染
Angular 16+ 默认启用水合后,[innerHTML] 绑定的内容会在服务端写入一次,客户端激活时又尝试“同步”该值。但 bypassSecurityTrustHtml() 返回的是不可序列化的 SafeHtml 对象,框架无法比对 DOM 状态,最终导致内容被插入两次。
- 典型现象:文本或富文本区块显示两遍,或 DOM 节点层级错乱
- 根本原因不是 XSS 防御逻辑出错,而是水合过程缺乏可比对的序列化状态
- 临时绕过方式是加
ngSkipHydration属性,但会丢失该节点的交互能力
renderToString 与 hydrateRoot 的参数必须严格匹配
React 18 的 hydrateRoot 不再接受字符串 HTML,必须用 renderToString 输出的结构 + 客户端同构组件树做比对。一旦服务端用了 renderToStaticMarkup(省略 React 特有属性),客户端就无法定位根节点,直接报 Hydration failed because the server rendered HTML didn't match the client。
React 与 Next.js 性能优化指南,源自 Vercel 工程团队。适用于编写、审查或重构 React/Next.js 代码时使用。
- 服务端必须用
renderToString,不能用renderToStaticMarkup - 客户端初始化必须传入和服务端完全相同的 props,包括
key、data-*属性、甚至空格和换行(影响文本节点顺序) - 第三方库如
react-i18next若未在服务端预设语言,会导致文本内容不一致
window.__INITIAL_STATE__ 注入时机决定水合成败
这个全局变量必须在 <script></script> 标签中、所有业务 JS 执行前完成赋值。如果放在 document.body 后面,或者被 Webpack 的 defer 脚本延迟加载,客户端 store 初始化时读到的就是 undefined,水合后状态为空,用户看到空白或默认态。
- 推荐写法:
<script>window.__INITIAL_STATE__ = ${JSON.stringify(state)};</script>,紧贴结束前 - 避免使用
JSON.stringify直接序列化含有Date、RegExp或循环引用的对象 - Vue 用户注意:
__NUXT__或__VUE_DEVTOOLS_GLOBAL_HOOK__类似机制也需同样处理
水合失败往往不抛错,只静默降级为客户端重渲染——这意味着你可能在本地开发时完全察觉不到问题,直到上线后首屏白屏或交互延迟才暴露。最隐蔽的坑不在代码逻辑里,而在 HTML 字符串的不可见差异中。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










