局部同构刷新需服务端与客户端在HTML结构、props序列化、hydration边界、错误恢复四方面严格一致,否则导致SEO失效或DOM重建。

局部同构刷新不是加个 useEffect 或套个 Suspense 就能跑通的事——它要求服务端与客户端在每个可刷新区块上达成“渲染契约”:HTML 结构、props 序列化、hydration 边界、错误恢复,四者缺一不可。做不到这点,轻则 SEO 内容被 JS 覆盖,重则整块 DOM 被 React 丢弃重建。
服务端必须直出可水合的完整 HTML 子树
搜索引擎不执行 JS,也不等 useSWR 返回数据。任何依赖客户端 fetch 的关键内容(如文章标题、摘要、面包屑),若没在服务端初始 HTML 中存在,就等于对爬虫不可见。
- 首屏关键区块(如
<article></article>、<nav></nav>)禁止包裹在React.lazy、Suspense或条件渲染中 - 局部刷新区域需有稳定
key,且服务端与客户端必须一致(如key={post.slug},禁用Math.random()或数组索引) - 服务端渲染时,该组件接收的 props 必须 100% 与客户端初始 props 匹配(包括
null和undefined的语义差异) - 推荐将服务端数据序列化后内联为
window.__INITIAL_DATA__,客户端直接读取,避免 hydration mismatch
客户端接管必须严格限定在 hydration 后的子树边界内
React 水合失败不会报错,只会静默降级为客户端渲染——你根本不知道哪块内容已经脱离 SEO 覆盖。所谓“局部”,本质是划定一个服务端可预测、客户端可安全接管的 DOM 范围。
SEO全流程:诊断、页面优化、技术SEO、内容策略、外链建设;触发关键词包括SEO、搜索排名、网站优化、关键词、标题描述、结构化数据、Core Web Vitals、sitemap、robots.txt、SEO审计等。
- 每个局部刷新区域应包裹在显式
<errorboundary></errorboundary>中,fallback 使用<div aria-live="polite"> 保证可访问性 <li>禁止用全局状态(如 <code>zustandstore)直接触发某组件 rerender,除非该 store 初始值已通过服务端同步注入 - 动态修改
document.title或meta标签需通过框架的setHeadAPI 或服务端模板变量静态注入,不能靠客户端 JS - 服务端日志中应明确标记“此响应含可水合区块:/comments、/related-posts”,用于线上监控 mismatch 率
- 服务端需实现
getServerSideProps(Next.js)或loader(Remix)预取首屏所需数据,确保 HTML 中已有真实内容 - 客户端后续刷新(如点赞、切换评论页)应走独立 API,但返回结构必须与服务端初始结构兼容(字段名、嵌套层级、空值处理一致)
- 避免在服务端用
fetch请求第三方不稳定接口——超时或错误会导致整页 SSR 失败;可用 fallback 数据兜底 - 流式渲染(
renderToNodeStream)可提升 TTFB,但需确保流中每个 chunk 都是合法 HTML 片段,否则 hydration 会中断
数据获取必须分层:服务端预取 + 客户端增量更新
局部刷新的数据源不能全扔给客户端。服务端要提供 fallback 内容,客户端只负责“增强”,而非“从零构建”。
最容易被忽略的点是:局部刷新区域的 CSS 作用域必须与服务端输出一致。CSS-in-JS 库(如 Emotion、Styled Components)若未正确提取服务端样式并内联,会导致 hydration 后样式闪动或丢失——这不是 JS 逻辑问题,而是服务端生成的 class 名与客户端不匹配所致。


















