题干与适用场景
一个 Next.js 16 电商平台有四类页面:
/guides/[slug]:20,000 篇公开指南,每周由内容团队更新,要求搜索引擎稳定发现正文;/products/[id]:100 万个公开商品页,描述和图片可接受最多 5 分钟陈旧,价格与库存必须在下单前实时确认;/search?q=:结果随查询、筛选和当前商品状态变化,要求首屏可分享,但不要求把每个查询组合收录;/account/orders:登录用户的私有订单页,数据按用户变化,不应被共享缓存或搜索引擎收录。
团队希望降低源站渲染量,同时保持公开页可发现、交互页响应及时。面试要求候选人给每类路由确定 主要渲染策略,再说明同一路由中哪些区域可以混用其他策略。必须交代新鲜度上限、失效触发、失败时 展示什么,以及如何证明选择在生产环境有效。
2026 年公开前端面试专题直接把“给定页面应选择哪种渲染策略”和“ISR 与 SSR 如何取舍”列为 问题;独立前端题库也单列 CSR、SSR、SSG 与 ISR 的比较。现有搜索结果大多停留在四行优缺点, 很少把缓存失效、关键字段实时校验、外置 CDN 和故障语义放进同一答案。本题补足的正是从术语 比较走到可上线决策的部分。
面试官考察点
第一,看候选人是否先定义页面需求。决策输入包括公开可索引性、首屏内容、新鲜度、个性化、流量、 页面基数、交互成本和失败行为。只说“SSR 有利于 SEO”无法决定订单页 或百万商品页该怎么做。
第二,看是否理解四种策略把成本放在哪里:
| 策略 | HTML 何时产生 | 主要优势 | 主要代价 | |---|---|---|---| | CSR | 浏览器运行 JavaScript 后 | 私有、强交互状态更新直接 | 初始内容依赖脚本与数据请求,客户端成本高 | | SSR | 每次请求时在服务端 | 可按请求返回新鲜或个性化 HTML | 延迟与可用性依赖渲染服务和上游,计算量随流量增长 | | SSG | 构建时 | 静态文件易缓存,源站请求少 | 发布与页面规模绑定,更新通常需要重新生成 | | ISR | 先静态生成,随后按时间或事件再生成 | 静态交付与局部更新兼得 | 存在明确的陈旧窗口、失效传播与再生成失败语义 |
第三,看能否识别混合方案。一个商品页可以用 ISR 提供可索引的商品描述,用客户端请求更新当前 配送选项,并在服务端下单接口重新校验价格与库存。选择 ISR 不表示整页每个字段都允许陈旧;选择 SSR 也不表示页面不需要客户端 JavaScript 或水合。
第四,看是否会把策略变成可验证契约。强回答会写出“最多陈旧多久”“谁触发失效”“再生成失败 展示什么”“缓存键是否包含用户或地区”“真实浏览器下载多少 JavaScript”,并给出构建、运行时、 缓存和业务正确性的指标。
回答前需要澄清的问题
- 页面是否公开且需要收录? 公开正文、标题、canonical 和状态码应尽量出现在初始响应中;
私有订单页应认证、禁止共享缓存,并明确 noindex 策略。
- “实时”到底多快? 商品描述允许 5 分钟陈旧,价格与库存却影响付款承诺。两个字段不能沿用
同一个缓存 SLA。
- 内容按什么维度变化? 语言、地区、币种、登录态和实验分组会改变缓存键。漏掉维度会造成
错内容甚至数据泄漏,维度过多则会打碎缓存命中率。
- 页面基数和更新分布怎样? 100 万个 URL 不适合每次发布全部构建;若 95% 长期没有访问,
可以只预生成热门路径,其余首次访问时生成。
- 发布事件可靠吗? CMS 或商品系统能否发送带实体 ID 的事件?若事件可能丢失,需要时间失效、
对账扫描或手动失效作为补偿。
- 平台如何部署缓存? 单实例、多个容器、托管平台和外置 CDN 的失效传播不同。只清除 Next.js
服务端缓存,不一定清除另一个 CDN 的副本。
- 失败时宁可旧还是错? 指南再生成失败时继续展示上一成功版本通常合理;价格、库存和订单
状态必须从权威服务确认,不能把旧缓存当承诺。
- 成功门槛是什么? 约定可索引内容完整率、TTFB/LCP/INP、缓存命中率、再生成延迟、陈旧年龄、
源站渲染 QPS、构建时长、错误率和下单校验冲突率。
30 秒回答框架
“我先按公开性、每请求差异、新鲜度和页面规模分路由。指南以 SSG 为主,发布时按路径失效;商品 详情用 ISR 提供可索引且低成本的主体,商品事件触发失效,并以 5 分钟时间失效兜底,价格与库存 在客户端显示时刷新、下单时由服务端再次确认。搜索结果按查询 SSR,并避免共享包含用户条件的 响应;私有订单页使用认证后的服务端壳和 CSR 数据更新。最后用生产构建验证缓存行为,监控陈旧 年龄、命中率、TTFB、LCP、JavaScript 体积、再生成失败和业务冲突,避免只看 Lighthouse 一次分数。”
分步骤深入解答
第一步:先用决策矩阵确定每个路由的主要策略
把每类页面放进同一张矩阵,避免从框架 API 倒推需求:
| 路由 | 主要策略 | 决策依据 | 混合部分 | |---|---|---|---| | 指南 | SSG + 按事件再生成 | 公开、更新少、正文对所有用户相同 | 发布后按路径失效;定期对账 | | 商品详情 | ISR | URL 多、公开、主体可短时陈旧 | 价格/库存实时读取,下单服务端再校验 | | 搜索结果 | SSR | 查询组合多、结果随请求变化 | 浏览器接管筛选与后续交互 | | 私有订单 | CSR 为主 | 按用户变化、不需索引、交互频繁 | 可服务端输出认证壳和首屏骨架 |
指南的“SSG + 发布失效”在运行行为上已经使用了增量再生成能力,但主要内容模型仍是预生成静态页。 这比给 20,000 篇正文每次请求做 SSR 更便宜,也比每次改一个错字就完整重建更及时。构建时可以 预生成全部指南;若数量继续增长,则只预生成热门路径,其余按首次请求生成并缓存。
商品详情选择 ISR,是因为主体内容公开、访问重复且允许有限陈旧。不能把“5 分钟 revalidate” 解释成任何情况下严格最多陈旧 5 分钟:时间窗口过后,下一次请求仍可能先拿到旧页面并触发后台 生成;低流量页面可能更晚才触发,生成失败也会延长旧版本寿命。若业务需要发布后尽快更新,商品 写入成功后应按商品 ID 触发失效,时间窗口只做补偿。
搜索结果的查询空间巨大,预生成收益低。SSR 可以在首个响应中给出与查询一致的内容和真实状态码, 但缓存必须区分会影响结果的公开参数;登录态、个性化价格等响应应标记为私有或不缓存。客户端 在首屏后继续管理筛选、分页和输入状态。
订单页无需用公开 HTML 换取索引收益。认证壳可以由服务端建立,但订单列表和实时状态以 CSR 读取用户 API,响应不得进入共享缓存。CSR 只是渲染选择,认证与授权仍由服务端执行。
第二步:把 ISR 写成新鲜度与失效契约
商品页需要两个层级的契约:
可缓存主体:名称、描述、图片、分类
事件失效:product:{id} 更新成功后触发
时间兜底:300 秒
失败语义:继续服务上一成功版本并告警
关键实时字段:价格、库存、配送资格
页面展示:从权威接口刷新并标注更新时间
下单提交:服务端重新计算并要求用户确认变更Next.js 的 ISR 文档说明,时间窗口结束后的首次请求可以收到旧内容,同时后台开始重新生成;成功后 后续请求得到新版本。若重新生成抛错,上一成功版本继续从缓存提供,下次请求再尝试。因此陈旧年龄 需要监控,不能把配置值当成已经实现的业务 SLA。
Next.js 16 中,revalidateTag(tag, "max")采用 stale-while-revalidate,适合允许短暂延迟的文章、 目录或商品主体。需要用户提交后立即读到自己写入时,Server Action 中的 updateTag提供 read-your-writes 语义。两者不能混成一个含糊的“清缓存”按钮;事件来源、可接受陈旧度和调用 环境决定使用哪一种。
还要画清缓存传播链:浏览器、CDN、Next.js 路由缓存、数据缓存和上游服务可能各有 TTL。官方 CDN 指引明确提醒,Next.js 的按路径或标签失效不会自动清除外置 CDN 已缓存的副本;若另加 CDN,必须 同时清除相应 HTML 和数据变体,或让其 TTL 满足同一新鲜度契约。多实例自托管还要共享缓存或同步 失效状态,避免一台实例更新、另一台继续提供旧内容。
第三步:分别处理 SEO、交互和故障边界
Google 能执行 JavaScript 并使用渲染后的 HTML 建立索引,但渲染会进入队列,其他机器人未必执行 JavaScript。公开指南和商品主体应在初始 HTML 中包含可见正文、标题、canonical、结构化数据和 正确状态码。不能给用户返回空壳 200,再靠客户端把不存在的商品改成“未找到”;服务端或静态 生成阶段就应返回真实的 404。
预渲染也不自动等于快速或可交互。SSR/SSG/ISR 页面如果发送过多客户端组件,仍会支付 JavaScript 下载、解析和水合成本;CSR 如果拆包合理、数据靠近用户,也可能在登录后的导航中表现很好。因此 同时测 TTFB、LCP、INP、长任务、下载与执行的 JavaScript,以及从可见到可交互的时间。
故障策略按数据风险分级:
- 指南和商品描述再生成失败:继续提供上一成功版本,展示内容更新时间,并告警陈旧年龄;
- 搜索上游超时:返回可重试错误或经过明确许可的短时缓存,不能伪装成“无结果”;
- 价格或库存接口失败:禁止确认旧值,结算前要求重试;
- 订单接口失败:保留已加载页面状态并提示重试,不能跨用户回退到共享缓存;
- 失效事件积压:按实体版本对账,找出缓存版本落后的页面并补偿失效。
第四步:用生产构建和业务指标验收
开发模式不能证明静态生成与 ISR 行为。先执行生产构建,再用生产服务器验证四类路由。测试至少 覆盖:
- 查看构建输出,确认预期路由是静态、按需生成还是动态,不因一次未缓存读取意外变成 SSR。
- 连续请求同一商品,验证缓存命中;修改商品后记录事件时间、失效时间和新 HTML 首次可见时间。
- 让再生成依赖失败,确认仍提供上一成功主体、错误被记录,恢复后能再次生成。
- 通过不同语言、地区、币种和登录态请求,确认缓存键完整且私有响应没有共享。
- 直接抓取初始 HTML,验证正文、metadata、canonical 和
404;再用真实浏览器检查水合与交互。 - 模拟外置 CDN 和多实例部署,确认失效传播到每一层,而非只在发出事件的实例生效。
- 在真实页面分布下测构建时间、源站渲染 QPS、缓存命中率、TTFB、LCP、INP 和 JavaScript 成本。
- 对比页面展示值与结算权威值,监控价格/库存冲突率,确保缓存优化没有改变交易正确性。
上线采用逐路由迁移。先观察当前 SSR 流量与正确性,再把低风险指南转为静态;商品 ISR 先以影子 方式记录“缓存会返回什么”,验证失效可靠后再服务流量。任何阶段都保留按路由回退到上一策略的 开关,并以业务正确性和陈旧年龄作为回退条件。
高质量示范回答
“我不会给整个应用只选一个缩写。指南公开、同质且更新少,我会构建为静态 HTML,CMS 发布成功 后按路径失效;这样初始正文可索引,绝大多数请求走静态缓存。商品页有 100 万个 URL,所以只预生成 热门商品,其余首次访问生成。商品主体使用 ISR,商品事件按 ID 失效,300 秒只做丢事件兜底。 时间到期后的首个请求可能仍拿到旧版本并触发后台生成,因此我会监控实体更新时间与缓存生成时间的 差值,而不会承诺配置了 300 秒就一定只旧 300 秒。”
“价格、库存和配送资格不继承商品主体的陈旧窗口。浏览器展示时从权威接口刷新;用户点击下单后, 服务端重新计算,并在价格变化时要求确认。搜索组合过多且随查询变化,首屏用 SSR,后续筛选由 客户端接管;任何包含登录态或个性化条件的响应都不进共享缓存。订单页使用认证后的壳和 CSR 数据, 服务端按用户授权,页面设置不收录。”
“验收时我会用 production build 检查每条路由的实际渲染类型,测试缓存命中、事件失效、300 秒 兜底、再生成失败和恢复。外置 CDN 需要与 Next.js 同步清除,多个实例需要共享失效状态。公开页抓 初始 HTML 检查正文、canonical 和状态码,再在真实浏览器测 TTFB、LCP、INP、JavaScript 成本。 最终还要比较页面价格与结算价格;性能通过但交易正确性下降,方案仍然失败。”
常见错误与改进建议
- 按应用只选一种策略 → 公开页、搜索和私有页需求不同 → 按路由甚至按数据区域决策。
- 把 SSR 等同于 SEO 保证 → metadata、状态码、canonical、可抓取链接仍可能错误 → **检查初始
HTML 与抓取结果。**
- 把 CSR 说成搜索引擎完全不可见 → Google 可以渲染 JavaScript,但存在排队和其他机器人能力
差异 → 公开关键内容优先服务端或预渲染,同时准确描述限制。
- 把
revalidate=300当硬性五分钟 SLA → 低流量、后台生成和失败都会延长陈旧 → **事件失效、
陈旧年龄监控与时间兜底一起使用。**
- 整页共享一个新鲜度 → 描述允许短时旧,价格与库存不能直接承诺 → **按风险拆分数据并在
交易边界重新确认。**
- 只清 Next.js 缓存 → 外置 CDN 或其他实例仍可能有旧副本 → 画出所有缓存层并验证传播。
- 认为预渲染必然更快 → 大量 JavaScript、水合和慢上游仍会拖慢体验 → **同时测网络、主线程、
Web Vitals 与服务端指标。**
- 只在开发模式测试 ISR → 开发行为不能代表生产缓存 → **使用 production build 和生产服务器
执行命中、失效与故障测试。**
- 用旧页面掩盖所有错误 → 搜索空结果、旧价格和跨用户订单会造成错误决策或泄漏 → **按数据
风险定义 stale-if-error。**
延伸追问
如果商品更新后必须在 10 秒内全球可见,ISR 还能用吗?
可以保留 ISR,但必须把 10 秒写成端到端失效 SLO:商品事务成功后可靠发出带版本的事件,所有 Next.js 实例同步标签失效,外置 CDN 同时清除,探针从多个地区检查新版本。时间再验证只能兜底, 不能单独保证 10 秒。若清理链无法达到目标,则对该字段使用动态读取或更短、可证明的缓存路径。
静态生成 100 万个商品为什么可能有问题?
全量生成会把构建时间、产物和发布风险与页面基数绑定,其中大量长尾页可能从未被访问。可以只 预生成高流量商品,其余首次请求时生成并缓存;同时控制并发生成、防止热门事件造成再生成风暴, 并保留不存在商品的可靠 404。
SSR 响应能否放进 CDN?
能否共享取决于响应是否对所有缓存键维度相同,而不是取决于“SSR”这个名字。公开搜索结果若键完整 且允许短时陈旧,可以谨慎设置共享缓存;包含 Cookie、用户权限、个性化价格或实验分组的响应应 私有或不缓存。必须测试 Vary、缓存键和登出后的跨用户隔离。
RSC、流式 SSR 和 PPR 会让四分法失效吗?
它们让同一路由可以更细地组合静态与动态工作,但没有消除决策输入。仍要回答内容在哪里执行、 初始 HTML 有什么、数据缓存多久、客户端收到多少 JavaScript、动态区域失败时如何降级。面试中先 把 CSR/SSR/SSG/ISR 的交付与新鲜度说清,再说明 RSC、流式输出或部分预渲染如何优化具体区域。
如何防止缓存失效风暴?
按实体聚合重复事件,用版本号丢弃过时事件,为再生成设置并发上限与抖动,并让同一缓存键只有一个 生成者。监控队列深度、生成时长、失败率和陈旧年龄;若积压扩大,优先保证价格库存等关键动态读取, 商品主体可以继续提供上一成功版本。