流式 SSR 是什么?它解决了什么问题?
简化版
流式 SSR 是服务器不等整页 HTML 全部生成完再返回,而是边渲染边把 HTML 分块发送给浏览器。它能更早输出页面骨架和已完成内容,降低用户等待感,适合数据依赖复杂或页面较大的场景。
详细版
传统 SSR 可能要等所有数据和组件渲染完成,才返回完整 HTML。如果某个非关键接口慢,会拖住整页。
流式 SSR 的思路:
- 先发送页面外壳和关键内容。
- 慢数据区域显示 fallback。
- 数据准备好后继续流式输出。
- 浏览器逐步接收和渲染。
优点:
- 更早开始传输 HTML。
- 降低 TTFB 或改善内容可见时间。
- 慢组件不一定阻塞整页。
代价是错误处理、缓存、SEO、监控和框架支持更复杂。
完整版教学
一、传统 SSR 的阻塞问题
如果整页有多个数据源,传统 SSR 常需要等所有数据完成后才能生成 HTML。
一个低优先级模块接口慢,可能让整个页面都迟迟不返回。这会让用户长时间空白等待。
二、流式 SSR 的核心
流式 SSR 把 HTML 当作流发送。服务器先输出已经准备好的部分,浏览器边接收边解析。
框架可以配合 Suspense 或类似机制,让慢组件先输出占位内容,数据回来后再补齐。
这让页面从“等整桌菜上齐再开饭”,变成“先上主菜,配菜慢慢补”。
三、复杂度
流式渲染对工程要求更高。中途出错时页面可能已经发出一部分,不能像普通 SSR 那样简单改状态码或返回错误页。
缓存也更复杂,因为页面不是一次性完整生成。监控也要区分首字节、首块内容、完整内容。
四、面试追问与工程落地
面试官可能问:“所有页面都适合流式 SSR 吗?”
不适合。简单页面、静态页面、数据很快的页面没必要引入复杂度。流式 SSR 更适合内容多、数据源多、部分模块慢但首屏关键内容可以先展示的页面。
工程中要先解决基础 SSR 稳定性,再考虑流式能力。
五、分块时间线与背压机制
假设页面外壳 50ms 可生成,主体 API 200ms,推荐 API 1500ms。传统等待全部约 1500ms 才开始返回;流式方案可在 50ms 发 shell、200ms 发主体、1500ms 再替换推荐 fallback。
50ms [HTML shell + fallback] ───────────────→
200ms [主体内容 chunk] ─────────────────────→
1500ms[推荐模块 chunk + 完成标记] ──────────→
| 指标 | 含义 | 流式优化目标 |
|---|---|---|
| TTFB | 首字节到达 | 尽早发 shell |
| 首个有意义块 | 关键内容出现 | 关键边界优先 |
| all content | 全部流结束 | 慢模块不阻首屏 |
| hydration ready | 交互可用 | 控制脚本与调度 |
服务器写入速度超过客户端读取速度时必须尊重 stream 背压,否则慢客户端会让缓冲区和内存持续增长。流式不是不断 write 而不看返回状态。
流式 SSR 的价值是重排等待:让关键内容先穿过管道;它不会自动缩短慢接口本身的 1500ms。
六、状态码、缓存、代理与 SEO 边界
首块发送后响应头和状态码通常已经锁定,因此 404、重定向、关键权限应尽量在 shell 前判断。后续边界失败只能发送局部错误标记、保持 fallback 或让客户端恢复,并记录服务器错误。
代理/CDN 可能缓冲小 chunk,导致服务器在 50ms 写出而浏览器 1500ms 才一次收到。验收要在真实网关后观察网络分块、压缩和 flush 行为,不能只在本地 Node 直连测试。
完整 HTML 缓存可以在首次流式生成后存储,后续直接命中;边生成边 CDN 缓存则依平台能力。缓存 key、失败的半截响应、断开清理和爬虫是否等待完整流都要验证。
数据边界设计:
关键身份/标题 ── shell 前完成
主体内容 ────── 首个 Suspense 边界
推荐/评论 ───── 后续边界或客户端加载
七、常见误区与追问
- 误区:流式 SSR 会让所有接口本身执行更快。 它让已完成内容提前发送,慢接口耗时并未消失。
- 误区:服务端每次 write 都会立刻到浏览器。 压缩器、代理、CDN 和 TCP 缓冲都可能合并 chunk。
- 误区:开始流式后还能随意改成 404/500。 头已提交时状态码无法正常改写,关键判断要前置。
- 追问:什么页面适合流式? 数据边界可拆、关键内容能先完成、非关键模块明显较慢的页面。
- 追问:如何处理客户端断开? 传播 abort,取消下游请求和渲染,释放流与请求级资源。
- 追问:背压为什么重要? 慢客户端消费不及写入时,无界缓冲会放大服务器内存。
- 追问:如何证明代理没有吞掉流式收益? 在生产链路抓取 chunk 到达时间,并对比直连与 CDN 后指标。
八、加强记忆
- 核心思想:HTML 分块输出,shell 和关键内容先到,慢模块后补。
- 边界划分:首屏关键数据、可延迟模块和客户端模块分层。
- 指标拆分:TTFB、首个有意义块、关键内容、全量完成、水合分别测。
- 协议约束:首块后状态码锁定,中途错误只能局部恢复。
- 流量控制:尊重背压,客户端断开要取消渲染和下游请求。
- 全链路验收:框架、压缩、代理、CDN、缓存和爬虫一起验证。