你走进一家餐厅,点了一份招牌菜。传统网页的做派很像“半成品外卖”:后厨并不炒菜,只给你寄来一袋食材(JS 脚本)和一份菜谱(渲染逻辑),让你回家自己开火。于是你盯着空荡荡的餐桌等了好久才吃上饭——这就是客户端渲染(CSR)的真实体验:浏览器先收到一个近乎空白的 HTML 壳子,再下载、解析、执行大量脚本,页面内容才姗姗来迟。更要命的是,搜索引擎派来的“美食评论家”(爬虫)习惯先看餐桌上有没有菜:桌上空空如也,它就认定这家店不营业,你的页面因此在搜索结果里“查无此店”。 SSR(服务端渲染)换了一种打法:后厨(服务器)接到点单后,先去食材仓库(数据库)取料,把整道菜炒好(渲染成完整 HTML),再端上桌。食客坐下就有菜吃,评论家也能把菜品拍得清清楚楚。Next.js 和 Nuxt.js,正是分别给 React 和 Vue 两套“后厨体系”量身定制的 SSR 方案。这篇文章就沿着“点单—备料—炒菜—上桌—保鲜—开店”这条线,把 SSR 的原理彻底讲明白。
掌握 SSR 原理,你就能在面试中讲清“首屏优化与 SEO”的底层逻辑,并能在实际项目中为 React/Vue 应用选出最合适的渲染与缓存方案——这是前端工程师奔向全栈的必经之路。
目录
可视化演示
展示SSR与CSR渲染流程对比、服务端数据获取、Hydration注水激活及SSG/ISR渲染模式选择,步进切换深入理解同构渲染机制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
SSR vs CSR:首屏速度与SEO对比 SSR服务端生成完整HTML,首屏快且SEO友好 CSR客户端渲染,首屏白屏长但交互流畅 FCP(SSR) ≈ 200ms vs FCP(CSR) ≈ 1500ms SSR首屏速度优势显著。
一、SSR基础概念:把菜做好再端上桌
餐厅里有两种上菜哲学。传统 SPA 是“半成品外卖”——浏览器先收到一个只有房梁的空房子(只含 root 节点的 HTML),再等 JS 菜谱下载完,才能现场搭灶炒菜。于是首屏时间里,食客只能盯着空盘子;而搜索引擎派来的“美食评论家”(爬虫)往往没耐心等 JS 执行,拍张空桌照就走,你的店在点评榜上从此查无此店。SSR 的哲学截然相反:“把菜做好再端上桌”。服务器直接吐出热腾腾的完整 HTML,首屏内容即刻可见,爬虫也能一眼看到全部菜品。
在技术世界里,这个“做好再端上桌”的动作叫服务端渲染。Next.js 是 React 生态的 SSR 框架,Nuxt.js 是 Vue 生态的 SSR 框架。它们都支持“同构”:同一份组件代码,既能在服务端被 renderToString 炒成 HTML 字符串,也能在客户端继续运行,维持 SPA 的顺滑体验。
SSR 的直接收益体现在两个指标上:首屏时间(FCP)与搜索引擎可见性。首屏时间是内容第一次呈现给用户的时刻;SEO 好坏取决于爬虫能否在不执行 JS 的情况下读到正文。SSR 从根上解决了这两点,代价是把渲染压力从浏览器转移到了服务器。
一句话总结:SSR 把“等待的环节”从食客眼皮底下挪到了后厨。食客坐下就有菜,评论家来了也有菜可拍——这正是 Next.js、Nuxt.js 被广泛选用的根本原因。
\displaystyle T_{\mathrm{SSR}}^{\mathrm{FCP}} = T_{\mathrm{render}}^{\mathrm{server}} + T_{\mathrm{network}}, \qquadT_{\mathrm{CSR}}^{\mathrm{FCP}} = T_{\mathrm{shell}} + T_{\mathrm{js}} + T_{\mathrm{render}}^{\mathrm{client}}- 服务端渲染耗时服务器调用渲染函数,把组件和预取数据加工成 HTML 字符串的时间,相当于后厨炒菜的时间。
- 网络传输耗时成品 HTML 从服务器传到浏览器的时间,接入 CDN 后这段会被大幅压缩。
- 空壳下载耗时CSR 中浏览器下载那个只含根节点、没有内容的 HTML 骨架的时间。
- 脚本加载耗时浏览器下载并解析执行 JS 包的时间,包越大这一段越致命。
- 客户端渲染耗时JS 执行完后,浏览器真正把内容画上页面的时间。
二、同构渲染流程:同一份菜谱,两个厨房都会做
“同构”这个词听起来高深,其实就是“同一份菜谱,两个厨房都能做”。后厨(服务器)先按菜谱做好一盘完整的菜端上桌(返回 HTML);随后,食客家自带的小厨师(客户端 JS)掏出同一份菜谱,在已有的菜上进行最后的摆盘与介绍——给每个按钮绑定点击事件、给输入框接上监听器。这个过程业界称为“水合”(hydration)。
一次完整的 SSR 请求生命周期大致是:请求到达 → 服务端执行数据预取 → 调用 renderToString 生成 HTML → 连同序列化数据一起返回 → 浏览器渲染出首屏 → 执行水合变成可交互应用。Next.js 的 getServerSideProps 与 Nuxt 的 asyncData 负责其中“备料”这一环。
水合是同构的关键。React 使用 hydrateRoot(container, <App/>),Vue 使用 createSSRApp(App).mount('#app', true)。它们不会重建 DOM,而是把事件处理器“挂”到服务端已经输出的 DOM 节点上,从而避免重复渲染。这正是同构的价值:一套组件代码,在两端干不同环节的活。
所以一份菜谱两端通用:后厨炒熟,食客家的厨师做最后装饰。理解了这条链路,你就掌握了 SSR 应用“首屏快、交互顺”的全貌。
- 组件与数据入参服务端把组件树和预取数据一起交给渲染函数,就像菜谱和食材一起交给厨师。
- renderToStringReact 在服务端把组件树输出成 HTML 字符串;Nuxt 对应的 Vue 能力是
renderToString或renderToStream。 - hydrate 水合客户端以这段 HTML 为蓝本,把事件监听器一一挂上,使其恢复交互能力。
三、数据获取机制:下单先核对食材
资深大厨炒菜前必定先看仓库:这道菜的食材够不够?SSR 框架也内置了这样的“查库”环节。Next.js 的 getServerSideProps 和 Nuxt 的 asyncData,就是两位“备料主管”:每次收到请求,先跑去食材仓库(数据库或第三方 API)核对并取回数据,再把数据交给组件去渲染。
这两个函数的共同点是“只在服务端执行”:每次请求都会调用,拿到的数据直接作为组件的 props。它们不会被打包进浏览器的 JS,所以里面可以放心写数据库连接、API 密钥等敏感逻辑——就像后厨的采购清单,绝不会贴到餐厅门口。预取时机也很讲究:必须在渲染开始前完成,否则厨师炒菜时才发现缺料。
取回的数据必须“随菜上桌”:框架会把 props 序列化成 JSON,注入 HTML 中的 <script> 标签(Next.js 是 window.__NEXT_DATA__,Nuxt 是 window.__NUXT__),客户端水合时直接读取,避免二次请求。注意,序列化意味着只能传递 JSON 兼容的数据:函数、Date 对象、类实例都不能直接传递,需要先做转换。
有了“备料主管”,食客吃到的每一道菜都是按当下菜单现取食材现炒的,数据永远新鲜;而这份“进货单”随菜一起送到食客手里,客户端拿过来就能对账使用。
- getServerSidePropsNext.js 的页面级导出函数,每次请求时在服务端执行,接收含 params/query/req 的 context 对象。
- 返回 props函数返回
{ props: {...} },这些数据会传给页面组件用于渲染。 - JSON.stringify把 props 序列化成 JSON 字符串注入 HTML,客户端免去第二次请求。
四、路由机制:菜单即文件
大餐厅的菜单上,每道菜都对应后厨一张菜谱卡。Next.js 和 Nuxt.js 的哲学正是“文件即路由”:你只需把菜谱卡(组件文件)放进 pages/ 目录,框架就自动生成对应的菜单项(URL)。想加一道菜?放一个新文件即可,完全不用手动改路由配置。
pages/index.js 对应首页 /,pages/about.js 对应 /about。动态菜名(比如“今日特价”每天在变)用方括号表达:pages/posts/[id].js 能匹配 /posts/1、/posts/2……其中的 id 会作为参数传入数据预取函数,让同一张菜谱卡服务无数道特价菜。
路由机制还包括嵌套路由(多层目录对应多级路径)与客户端导航。内置的 <Link>(Next)和 <NuxtLink>(Nuxt)会拦截跳转,在浏览器端用 JS 局部更新页面,而不是整页刷新——就像同一桌加菜,不必重新铺桌布、换餐具。
菜单即文件,让项目结构一目了然:想上什么菜就放什么菜谱卡。配合动态段,一道模板就能服务无数个“今日特价”,这正是内容型站点最爱的路由方式。
- pages 目录约定式路由的根目录,一个文件对应一条路由规则。
- 动态段 [id]方括号声明这是一个动态参数,可匹配任意具体值。
- URL 参数id:框架把匹配到的值放入
context.params,供getServerSideProps或asyncData读取。
五、渲染模式:三种上菜节奏
餐厅上菜有三种节奏:现点现炒(CSR)、宴席冷盘提前备好(SSG)、陈列柜里的招牌菜定时换新(ISR)。CSR 适合纯交互应用;SSG 适合内容几乎不变的信息页;ISR 则是“折中大师”,既想快又想保持新鲜。
Next.js 用 getStaticProps 实现 SSG:构建时就把页面渲染成静态 HTML,部署后谁访问都是同一份“冷盘”,速度最快。如果给 getStaticProps 加上 revalidate 字段,就成了 ISR:页面先以旧版本响应(先从柜里拿),同时后台悄悄生成新版本。Nuxt 同样支持静态生成与按需渲染,模式选型的思路一致。
选型的关键是“数据新鲜度 vs 响应速度”的权衡:内容越静态(文档、博客)越适合 SSG;数据越个性化(用户中心、实时看板)越需要 CSR 或 SSR;处于中间的(商品页、资讯页)正是 ISR 的主场。
三种节奏没有高下,只有合不合适:宴席用冷盘,快餐现炒,招牌菜定时换新。架构师的任务,就是根据每道菜(页面)的特性,挑对节奏。
- age页面从生成到现在经过的时间,相当于菜品在陈列柜里存放的时长。
- revalidate开发者设定的最长保质期(秒),到期后的下一次访问触发后台重建。
- 分支判断未过期直接返回旧页;已过期则返回旧页的同时,在后台重新生成新页并替换缓存。
六、缓存策略:保温柜与出餐口
后厨再快,也架不住每桌都现炒。聪明的餐厅会在出餐口放一排保温柜:做好的菜先放进去,食客点了直接端走,只有柜里没有时才通知后厨重炒。对应到 Web 架构里,保温柜就是 CDN 缓存和页面缓存。
缓存分两层:页面级缓存把整个 HTML 存在 CDN 边缘节点,用户就近取用,连服务器都不用惊动——相当于在食客楼下开了家“分店柜”;数据级缓存则把数据库查询结果(如商品列表)存进 Redis,让“备料”环节也快起来。两者叠加,能极大缓解 SSR 的服务器压力。
缓存的关键是失效机制。Next.js 的 ISR 本质就是 stale-while-revalidate:先返回旧缓存,再异步更新。配合 HTTP 响应头 Cache-Control: s-maxage=60, stale-while-revalidate=300,CDN 也能参与这场“换菜”游戏。缓存命中率越高,平均响应时间越短,但数据新鲜度也越难保证——这是缓存永远的两难。
保温柜不是万能的:放太久菜会凉(数据过期),不放又累死后厨。SSR 工程师的日常,就是在这两个极端之间反复调优。
- h 命中率请求恰好能在保温柜里找到菜的概率,越高整体响应越快。
- T_cache 读缓存从 CDN 或 Redis 取回页面/数据的时间。
- T_gen 重新生成未命中时,服务端重新渲染页面、重新查询数据的时间。
七、部署方式:三种开店模式
有了菜谱和菜品,接下来要考虑“开店选址”。第一种:自己租厨房开堂食店——把 Node 服务常驻运行,用 next start / nuxt start 启动,灵活但租金贵(要自己管服务器);第二种:找中央厨房代工——把渲染函数交给 Serverless 平台(Vercel、Netlify),按单收费、弹性伸缩;第三种:只做外卖配送——构建时全部生成静态文件,丢到 CDN 上,零服务器成本。
Node 常驻服务器适合强动态、强定制的应用(如后台系统、需要 WebSocket 的业务),但需要自己操心扩容与运维。Serverless 适合流量波动大的公网应用,按请求计费,冷启动是它的小瑕疵。纯静态托管只支持 SSG 产物,用最快的速度换最弱的能力。
部署方式本质上是与渲染模式配套的:SSG/ISR 页面可全部静态托管,SSR 页面则必须运行 Node 或 Serverless 函数。Next.js 的 next export 只导出静态产物;包含动态接口的应用要用 next start 或部署到 Vercel 等平台。选择时权衡三个维度:动态能力、响应速度、运维成本。
开店没有最好的模式,只有最合适的选址。理解了三种形态的取舍,你就能在“项目该往哪部署”的争论里给出理性答案。
- Node 常驻一个长期运行的 Node 进程处理渲染请求,响应稳定但成本固定。
- 按需拉起请求到来时才启动函数执行渲染,空闲期零成本,但有冷启动延迟。
- 纯静态构建期一次性生成所有 HTML,部署后只有文件读取,最快也最受限。
AI 应用场景
电商大促商品页的 SSR+ISR 提速
阶段:AI 推荐结果的内容呈现与分发阶段
解决的问题:传统做法是前端先拉推荐接口、再渲染商品列表,首屏要等两次网络往返;页面内容也难被搜索引擎收录。用 Next.js 的 SSR 在服务端完成推荐数据拉取与页面渲染,首屏直接呈现实时推荐结果;再叠加 ISR 与 CDN 缓存,大幅降低服务器压力。这就像后厨把 AI 大厨配好的菜直接端上桌,而不是把菜谱丢给食客。
具体案例:某电商平台大促期间,商品详情页改用 SSR + ISR(revalidate=60)+ CDN 缓存后,首屏 FCP 从 3.2 秒降到 0.9 秒,商品页收录量提升 40%,服务器峰值负载下降 60%。
AI 资讯摘要站的 Nuxt 静态化发布
阶段:AI 生成内容的静态化与增量发布阶段
解决的问题:编辑用大模型批量生成新闻摘要后,如果每个用户请求都实时调用模型,成本和延迟都不可控。改用 Nuxt 的 SSG 在构建期把摘要生成静态 HTML,再通过定时构建或 ISR 增量更新,让“AI 写稿—自动发布—用户秒开”全链路自动化。
具体案例:某资讯聚合站用 Nuxt 静态生成 + 每 10 分钟增量构建,支撑日均十万级 PV,服务器零常驻进程,CDN 成本仅为传统部署的十分之一。
智能客服工作台的 SSR 预取与水合
阶段:AI 会话数据的服务端预取与交互激活阶段
解决的问题:客服工作台需要登录态和实时会话数据,纯 CSR 首屏白屏严重。SSR 在服务端完成会话历史预取并渲染首屏,客户端水合后立刻可打字、可点击,兼顾“秒开”与“可交互”。
具体案例:某 SaaS 公司的智能客服工作台用 Next.js 重写后,首屏加载从 4.5 秒降至 1.2 秒,客服人员每日接待量提升 35%。
Python 演示
# -*- coding: utf-8 -*-# MiniSSR: a tiny SSR + ISR cache simulator# Reference: Next.js getServerSideProps / Nuxt asyncDataimport time class MiniSSR: """A tiny SSR server: render -> cache -> revalidate""" def __init__(self, revalidate=5): self.revalidate = revalidate # 保质期(秒), 相当于 ISR 的 revalidate self.cache = {} # 保温柜: {product_id: {html, ts}} def fetch_data(self, product_id): """模拟数据库查询, 耗时 0.3 秒""" time.sleep(0.3) return { "id": product_id, "name": "product-%d" % product_id, "price": product_id * 10, } def render_html(self, data): """模拟 renderToString: 组件+数据 -> HTML 字符串""" return ( "<html><body>" "<h1 id='title'>" + data["name"] + "</h1>" "<p id='price'>" + str(data["price"]) + "</p>" "</body></html>" ) def render(self, path): """处理一次请求: 返回 (html, 缓存模式)""" product_id = int(path.split("/")[-1]) now = time.time() cached = self.cache.get(product_id) if cached is not None and now - cached["ts"] < self.revalidate: return cached["html"], "cache-hit" data = self.fetch_data(product_id) # 相当于 getServerSideProps 取数 html = self.render_html(data) # 相当于服务端渲染成 HTML self.cache[product_id] = {"html": html, "ts": now} # 更新保温柜 return html, "re-render" def main(): server = MiniSSR(revalidate=5) for i in range(4): html, mode = server.render("/product/42") print("[%d] %s -> %s..." % (i + 1, mode, html[:30])) time.sleep(3) if __name__ == "__main__": main()
self.revalidate = revalidate设定菜品“保质期”,对应 ISR 的revalidate配置self.cache = {}保温柜,存放已渲染好的页面 HTML 和生成时间戳time.sleep(0.3)模拟数据库查询的 0.3 秒延迟cached = self.cache.get(product_id)先看保温柜里有没有现成的菜if cached is not None and now - cached["ts"] < self.revalidate:判断是否过期return cached["html"], "cache-hit"没过期直接端菜,后端零负担data = self.fetch_data(product_id)相当于getServerSideProps在服务端取数self.cache[product_id] = {"html": html, "ts": now}现炒的菜放回保温柜
常见误区
误区“SSR 不就是像 JSP、PHP 那样在服务端拼 HTML 字符串吗?页面一跳一跳的,跟现代前端框架有什么关系?”
正解SSR 是同构渲染:同一套组件代码在服务端输出 HTML(解决首屏和 SEO),随后客户端执行水合接管 DOM,之后依然是 SPA 的无刷新体验。区别在于,老模板只负责“出字符串”,同构还负责“交互接管”。你可以理解为:老店把菜炒好就完事,新店炒好菜还会派服务员上桌帮你倒水、介绍菜品。
为何常见:只看到“服务端返回 HTML”这一表象,忽略了“水合”这个关键环节,于是把 SSR 和十几年前的模板引擎画了等号。
误区“我的项目用了 Next.js,那它所有页面都是服务端渲染的吧?性能肯定没问题。”
正解框架提供的是“能力”而非“默认行为”。Next.js 页面默认可能是 SSG 或 CSR,只有写了 getServerSideProps(或动态接口页面)才会真正 SSR;Nuxt 2 默认是 universal 模式,但也需要正确配置。判断一个页面是不是 SSR,要看数据获取函数和构建产物,而不是看用了哪个框架。
为何常见:把“框架具备的能力”等同于“框架默认的行为”,就像买了烤箱就以为家里每顿饭都是烤出来的。
误区“SSR 首屏快,所以 SSR 应用整体性能一定碾压 CSR,能上 SSR 就上 SSR。”
正解SSR 优化的是“首屏看到内容”和"SEO",代价是 TTFB(首个字节)变慢——服务器要等渲染完才能响应;服务器压力增大;水合仍然要下载执行 JS。在交互密集、数据个性化的场景(如聊天工具、后台编辑器),CSR 体验反而更好。性能要看具体指标和场景,不能只看首屏。
为何常见:用单一指标(首屏)代表整体性能,忽略了响应时间、服务器成本和交互路径的权衡。
误区“`getServerSideProps` 返回什么,客户端组件就能原样拿到什么,函数和 Date 对象当然也能传。”
正解数据要经 JSON 序列化注入 HTML 再被客户端读取。函数、类实例、Date 对象、循环引用都无法原样传递——Date 会被转成字符串,undefined 会被丢弃。想在两端共享非普通对象,必须显式做转换。这就像厨房里的活鱼不能直接装进外卖盒,得先宰杀处理好。
为何常见:以为 props 是“内存直接传递”,忽略了中间隔着一次 JSON 序列化的“打包运输”。
误区“水合就是客户端把页面重新渲染一遍,所以 SSR 页面在交互前总会闪一下。”
正解水合并不重建 DOM,而是复用服务端输出的 DOM 节点、只绑定事件处理器。React 的 hydrateRoot 与 Vue 的 hydrate 都会尽力匹配已有 DOM。真正的闪烁多来自 CSS 加载时机不当或服务端与客户端状态不一致(hydration mismatch),而不是水合本身。
为何常见:把“水合”误解为“重新渲染”,忽略了它“复用已有 DOM”的本质——它是给菜摆盘,不是把菜倒掉重炒。
面试题
Q1. 关于 SSR 对首屏和 SEO 的作用,下列哪项说法正确?
- A.SSR 让浏览器提前下载缓存 JS 脚本
- B.SSR 返回含完整内容的 HTML 文档← 正确答案
- C.SSR 把页面渲染交给浏览器端完成
- D.SSR 只用于后端接口快速开发
答案:B。SSR 的核心是服务端把组件渲染成完整 HTML 再返回,浏览器拿到即可展示,爬虫也能直接读取正文。A 描述的是资源缓存优化而非 SSR;C 是 CSR 的做法;D 把 SSR 误解成纯后端技术,忽略了它与前端组件的同构关系。
Q2. 水合(hydration)的核心作用是什么?
- A.在服务端重新渲染一遍整棵组件树
- B.让静态 HTML 绑定上交互事件处理器← 正确答案
- C.把组件树序列化为 HTML 字符串
- D.压缩服务端返回的 HTML 体积大小
答案:B。水合是在服务端输出的 DOM 上“挂上”事件监听器,复用节点而不是重建,相当于给端上桌的菜做摆盘与介绍。A 是 renderToString 的职责;C 是服务端渲染阶段;D 属于压缩优化,与水合无关。
Q3. 在 Next.js 中,下面哪个函数会在每次请求时都执行?
- A.getServerSideProps 每次请求执行← 正确答案
- B.getStaticProps 构建时执行
- C.getStaticPaths 路径枚举
- D.useEffect 客户端执行
答案:A。getServerSideProps 是 Next.js 的 SSR 数据预取函数,每次请求都在服务端运行,保证数据新鲜。B 和 C 只在构建期执行,属于 SSG;D 是 React 客户端生命周期钩子,与请求级数据预取无关。
Q4. 在 Nuxt.js 2 中,负责服务端数据预取的钩子函数是?
- A.asyncData 服务端预取数据← 正确答案
- B.mounted 页面挂载后执行
- C.created 组件创建时执行
- D.computed 计算派生属性
答案:A。Nuxt 的 asyncData 在服务端渲染阶段执行,返回的数据会合并进组件 data,对应 Next.js 的 getServerSideProps。mounted/created 是客户端生命周期钩子,computed 是计算属性,三者都不承担服务端预取职责。
Q5. 下面哪个场景最适合使用 SSG 静态生成?
- A.博客文章与产品文档站点← 正确答案
- B.实时股票行情报价页面
- C.用户个人动态信息流
- D.在线聊天消息会话窗口
答案:A。SSG 适合内容稳定、不依赖用户身份和实时数据的页面,像宴席冷盘提前备好。股票、动态流、聊天都需要高频个性化数据,应选择 SSR 或 CSR,否则用户看到的永远是过期的“旧菜”。
Q6. ISR 配置 revalidate=60 表示什么意思?
- A.每 60 秒强制在后台重建一次
- B.超 60 秒后先返回旧页再后台重建← 正确答案
- C.缓存中的数据最多保留 60 秒
- D.60 秒内页面不允许再次被访问
答案:B。ISR 采用 stale-while-revalidate 策略:age ≤ 60 秒直接返回缓存;超过后先把旧页给用户,同时后台生成新页。A 的“每 60 秒强制重建”错在重建是被过期后的访问触发的,而非定时发生;C 混淆了缓存语义;D 更是完全错误的限制。
Q7. 关于同构渲染(Isomorphic Rendering),下列说法正确的有?
- A.同一套组件代码在两端共用运行← 正确答案
- B.服务端把组件渲染成 HTML 字符串← 正确答案
- C.客户端水合复用服务端已有 DOM← 正确答案
- D.服务端和客户端各维护一套代码
答案:ABC。同构 = 一套代码 + 服务端出 HTML + 客户端水合。就像同一份菜谱,后厨和食客家都能做,只是分工不同。D 恰恰相反,是传统前后端分离“两套代码”的做法,与同构背道而驰。
Q8. 相比 CSR,SSR 可能带来哪些劣势?
- A.服务器承受更大的渲染压力← 正确答案
- B.TTFB 响应时间会明显变长← 正确答案
- C.首屏白屏时间会大幅增加
- D.搜索引擎更难抓取页面内容
答案:AB。SSR 把渲染搬到服务端,服务器压力变大、TTFB(首个字节)变长,这是“后厨集中炒菜”的代价。C 和 D 恰恰是 SSR 要解决的痛点:SSR 减少白屏、利于抓取,把它们说成劣势就颠倒因果了。
Q9. Next.js 中 pages/posts/[id].js 会匹配哪个路径?
- A.路径 /posts/23 动态文章页← 正确答案
- B.路径 /posts/23.js 文件路径
- C.路径 /pages/posts/23 目录路径
- D.路径 /posts?id=23 查询参数
答案:A。[id] 是动态路由段,/posts/23 中的 23 会进入 context.params.id,供数据预取函数使用。B 是文件系统路径的误解;C 混入了 pages 目录名;D 是查询字符串,不是路径匹配。
Q10. 某商品页用 revalidate=60 的 ISR 部署,并配置 CDN 响应头 Cache-Control: s-maxage=60, stale-while-revalidate=300。下列说法正确的有?
- A.60 秒内 CDN 直接返回缓存无需回源← 正确答案
- B.超过 60 秒后 CDN 可能触发回源刷新← 正确答案
- C.旧内容在最多 300 秒内可被继续使用← 正确答案
- D.该页面必须部署在 Node 常驻服务器
答案:ABC。s-maxage=60 表示 CDN 缓存 60 秒,期间直接返回无需回源;过期后进入 stale-while-revalidate:最多容忍 300 秒的旧内容,同时后台回源刷新——这与 ISR 的“先旧后新”完全一致。D 错误:过期页面的重新生成可以由 Serverless 或边缘函数完成,不强制要求 Node 常驻。此题检验的是 CDN 失效头与 ISR 的协同理解,属于 SSR 工程化的进阶考点。