SSR(Server-Side Rendering,服务端渲染)同步输出页面时,不一定会在 HTML 底部附带一大段 JSON。更准确地说:SSR 的核心任务是先在服务器端生成可访问、可展示的 HTML;底部 JSON 通常是为了把服务端已经拿到的数据交给客户端 JavaScript 继续使用,它是实现选择,不是 SSR 的硬性要求。

先给结论:底部 JSON 是可选的
如果页面只是静态展示,或者交互逻辑不需要复用服务端数据,服务器完全可以只返回渲染后的 HTML,而不额外输出大块 JSON。用户仍然能看到首屏内容,搜索引擎也能读取页面主体。
但在 React、Vue、Nuxt、Next.js 等需要客户端接管的场景里,框架经常会把初始状态写进页面,例如 window.__INITIAL_STATE__、__NEXT_DATA__ 或 type="application/json" 的脚本块。这样客户端启动后就能复用同一份数据,避免重新请求一次接口,并让页面从“已渲染”平滑进入“可交互”状态。
为什么有些 SSR 页面底部会有大段 JSON
常见原因主要有三类:
- 用于 Hydration:客户端框架需要知道服务端渲染时使用了哪些 props、路由信息和状态,才能把事件绑定到已有 HTML 上。
- 避免重复请求:服务端已经查过数据库或接口,如果客户端启动后再查一遍,会增加延迟和后端压力。
- 保留页面上下文:分页、筛选条件、用户态、推荐列表、AB 实验参数等信息,可能需要随页面一起交给浏览器。
所以,看到底部 JSON 并不代表“同步渲染必然如此”,它更多说明这个页面还承担了客户端应用接管、状态恢复或数据复用的任务。
几种常见实现方式
方式一:只返回 HTML,不注入初始 JSON
适合内容展示型页面、交互很少的专题页、文章页,或者客户端不需要立即接管状态的页面。
<body>
<article>
<h1>文章标题</h1>
<p>这里是服务端已经渲染好的正文内容。</p>
</article>
</body>
方式二:把必要初始数据写入页面
适合需要客户端继续渲染、绑定事件、复用首屏数据的页面。注意这里的数据应该尽量精简,只放客户端真正需要的字段。
<div id="app"><!-- 服务端渲染后的 HTML --></div>
<script>
window.__INITIAL_STATE__ = {
title: "Hello SSR",
contentId: 1087
};
</script>
方式三:首屏 HTML + 客户端按需请求接口
如果数据较大、实时性强,或者不适合直接暴露在 HTML 中,可以只输出首屏必要内容,后续交互再由浏览器请求 API。
fetch("/api/article/1087")
.then(response => response.json())
.then(data => {
// 按需更新页面状态
});
是否应该输出 JSON,要看这几个判断点
可以按下面的标准取舍:
- 页面是否需要 Hydration:需要客户端框架接管时,通常要传递一部分初始状态。
- 数据是否会被客户端继续使用:如果只用于生成 HTML,没必要完整塞到页面底部。
- 数据体积是否过大:大 JSON 会增加 HTML 体积,影响首包和解析成本。
- 是否包含敏感字段:任何写进 HTML 的数据都默认会被用户看到,后台字段、权限信息、内部标识要先过滤。
- 缓存策略是否匹配:HTML 与 JSON 混在一起时,页面缓存更新会变得更重;拆成 API 后可分别缓存。
实践建议
对于普通内容页,可以优先保证服务端 HTML 完整、语义清晰,再只输出客户端接管所需的最小状态。不要把数据库原始字段、完整接口响应或后台配置一股脑塞进页面底部。
如果页面交互复杂,可以保留初始 JSON,但建议做字段裁剪、压缩和安全过滤;如果数据非常大,则更适合拆成接口按需加载。这样既能保留 SSR 的首屏优势,也能避免 HTML 被无意义的数据拖慢。
总之,SSR 同步渲染和“页面底部大段 JSON”不是绑定关系。前者解决首屏 HTML 的生成问题,后者解决客户端状态交接问题。是否需要这段 JSON,应该由页面交互、框架机制、性能预算和安全要求共同决定。