题干与适用场景
内容页包含导航、正文、推荐、评论和个性化模块。正文应尽快可见,慢模块不能阻塞首字节,也不能让一个服务故障拖垮整页。请设计基于 Suspense 的流式 SSR,说明服务端如何发送 shell、边界如何逐块完成、失败如何降级,以及客户端脚本加载后如何恢复交互。
React 文档说明,流式渲染可以先发送 shell 和 fallback,再在边界完成后替换内容;题目考察候选人能否把这个机制落成可控的超时、错误、缓存和监控方案。
面试官考察点
重点包括稳定 shell 与异步边界的划分、数据请求去重、边界超时、服务端错误处理、客户端重试、缓存变体、取消请求、可访问性和 Core Web Vitals。还要说明哪些内容必须同步完成,哪些内容适合延迟。
回答前需要澄清的问题
- 首屏目标是 TTFB、LCP 还是可交互时间,预算分别是多少?
- 正文、推荐、评论和个性化数据的可用性等级与隐私边界是什么?
- 页面是否经过 CDN 缓存,用户、地区和实验分流会产生哪些变体?
- 慢模块失败时显示空状态、旧缓存还是重试按钮?
- 客户端 JavaScript 失败时,哪些功能仍必须可用?
30 秒回答框架
“先发送不依赖慢数据的可访问 shell,把推荐、评论和个性化拆成有明确预算的 Suspense 边界。每个边界都有服务端超时、可观测错误和可接受 fallback;失败只影响该块。响应结束后客户端接管重试和交互。缓存只缓存安全的公共片段,监控 TTFB、LCP、INP、错误率和边界耗时。”
分步骤深入解答
第一步:划分 shell 与边界
同步完成路由、标题、正文骨架和主要语义结构;将推荐、评论、个性化拆成独立边界。边界应对应用户可理解的区域,避免一个边界同时包含多个互不相关的远程依赖。
shell: navigation + heading + article outline
boundary A: recommendations, budget 300 ms
boundary B: comments, budget 500 ms
boundary C: personalized actions, private and uncached每个边界的 fallback 保留标题、尺寸和语义,防止完成时发生布局位移;不要为了追求更多并行而把页面切成难以理解的碎片。
第二步:建立流式响应与取消策略
使用框架提供的流式服务端渲染 API,让 shell 先进入响应,再让边界随数据完成。服务端请求要携带请求级 deadline;超时后停止远程调用并输出可接受的 fallback,客户端不应继续等待已经取消的服务端任务。
记录每个边界的开始、完成、超时和错误事件。客户端断开时取消尚未完成的 fetch,避免用户离开后继续消耗数据库或推荐服务资源。
第三步:处理服务端与客户端错误
服务端边界错误应被隔离到对应 fallback,保留 shell 和其他已完成内容。错误需要带稳定的边界标识和请求追踪 ID,但用户文案只说明内容暂时不可用。
客户端加载后,允许对可重试边界发起带退避的请求;重试必须有次数上限、幂等条件和取消入口。客户端脚本加载失败时,正文、链接和表单等核心路径仍应保持可用。
第四步:设计缓存边界
公共正文和不含用户数据的模块可以按路由、语言、地区和内容版本建立缓存键。个性化边界不得混入公共 HTML 缓存;实验分流必须显式进入键或在边缘层完成隔离。
缓存命中不能掩盖源数据过期。为片段记录生成时间、失效时间和版本,命中旧数据时展示一致的更新时间策略。流式拼接后的整页缓存要谨慎,优先缓存安全的片段。
第五步:保护可访问性与布局稳定
fallback 与最终内容使用相同的语义标题、区域标签和尺寸约束。替换内容不能让键盘焦点跳到不可见节点;动态区域应有合适的状态提示,避免频繁朗读整页。
为图片和媒体预留尺寸,使用稳定的骨架布局;监控 CLS。正文的关键内容应尽量在首个可视边界内完成,不能把 LCP 元素放入不可控的长尾请求。
第六步:建立发布与观测门禁
预发布测试覆盖慢依赖、500、断流、客户端脚本失败、缓存污染和取消请求。线上按路由、边界和依赖统计 TTFB、LCP、INP、边界 p50/p95、超时率、fallback 率和重试成功率。
灰度时先观察 shell 和关键边界,再逐步开放高风险个性化模块。若错误率或长尾耗时超过阈值,回退到上一版边界编排或静态 fallback,而不是继续扩大流量。
高质量示范回答
我会先定义首屏预算和页面可用性等级,再把正文 shell 与推荐、评论、个性化拆成独立 Suspense 边界。每个边界有超时、fallback、取消和追踪 ID;失败只影响该区域。公共片段按安全维度缓存,个性化内容不进入公共缓存。发布前用断流和脚本失败测试,线上同时看 LCP、INP、CLS、边界 p95 与 fallback 率。
常见错误
- 把整页包在一个边界中 → 最慢依赖阻塞所有内容 → 按用户可理解区域拆分。
- fallback 没有固定尺寸 → 内容替换造成 CLS → 预留尺寸并保持语义结构。
- 将个性化 HTML 放入公共缓存 → 用户数据泄露 → 将私有片段隔离并纳入缓存键。
- 服务端超时后客户端无限重试 → 放大依赖压力 → 设置预算、退避、上限和取消。
- 只测成功路径 → 断流或脚本失败时整页不可用 → 验证错误、取消和无脚本降级。
追问及应对
追问一:边界越多越好吗?
不是。边界应对应独立的用户区域和故障域;过细会增加 fallback 噪声、监控成本与缓存变体,过粗则扩大阻塞范围。
追问二:如何保证错误不会中断整条流?
让错误落在边界的服务端与客户端恢复路径中,保留已发送 shell,并为每个边界提供稳定 fallback。还要测试响应已经部分发送时的异常。
追问三:哪些指标最能证明方案有效?
同时看 TTFB、LCP、INP、CLS、边界 p95、超时率、fallback 率和重试成功率;单看平均响应时间会掩盖长尾和局部故障。
追问四:如何避免缓存泄露实验或用户数据?
把用户、实验、地区、语言和内容版本等安全维度显式纳入键;无法证明安全的片段不进入公共缓存,并用跨用户回放测试验证。