Next.js SSR 性能实测:为什么服务端渲染不等于更快

Next.js SSR 性能实测:为什么服务端渲染不等于更快 “首屏慢改成 SSR 吧。”这是一个常见但信息不足的性能判断。SSR 只说明 HTML 在请求期间由服务器生成。数据等待、服务器计算、缓存、客户端 JavaScript 和 hydration 仍可能把成本放到不同链路。本文用 Next.js 16 的同页面对照实验分别观察 TTFB、LCP 和完整内容就绪回答“SSR 到底改变了什么”。问题是SSR 改变的只是“内容在哪里、什么时候生成”。用户体验还会同时受到数据等待、服务器计算、缓存、网络、客户端 JavaScript、hydration、图片和设备性能影响。如果不先拆开这些变量“用了 SSR”只是架构描述不是性能结论。先澄清Server Component 不等于每次请求 SSR在当前 Next.js App Router 中页面和布局默认是 Server Components。官方文档说明它们可以在服务端取数、缓存结果并流式发送需要状态、事件、生命周期或浏览器 API 时再用 Client Components 增加交互。但“组件在服务端执行”并不表示“页面每次请求都重新生成”。同一个 Server Component 路由可能在构建时预渲染也可能使用缓存结果还可能等到请求到来后动态渲染。Next.js 16 的 Cache Components 还允许在同一路由内组合静态壳、缓存内容和由 Suspense 延后到请求时渲染的动态片段。所以讨论性能前至少要把三个概念分开概念它回答的问题Server / Client Component代码主要在哪个环境执行、哪些部分需要客户端 JavaScript静态 / 请求时 / 客户端渲染完整内容在构建、请求还是浏览器阶段生成缓存与再验证已生成的数据或输出可以复用多久、何时失效把三者混成一句“上 SSR”很容易优化错位置。我做了一个只改变渲染位置的对照实验为了观察渲染位置本身的影响我用 Next.js 16.2.12 做了三条路线静态生成构建时等待同一份固定数据并生成完整 HTML请求时渲染每次请求到来后在服务器等待数据并生成完整 HTML客户端渲染先返回静态页面壳体浏览器再请求数据并生成完整内容。三条路线使用相同的数据、DOM、组件、样式和 180 ms 模拟数据延迟。差别只有等待发生在构建、请求还是浏览器阶段。静态路线没有使用特殊静态 API构建器可以把它预渲染export default async function StaticPage() { const data await getExperimentData(); return ExperimentView data{data} modestatic /; }请求时路线使用connection()明确等待真实请求。Next.js 官方文档将它定义为一种排除构建时预渲染、转入运行时动态渲染的方式import { connection } from next/server; export default async function DynamicPage() { await connection(); const data await getExperimentData(); return ExperimentView data{data} modedynamic /; }客户端路线先显示加载状态再在useEffect中请求同一份数据use client; export function ClientExperiment() { const [data, setData] useStateExperimentData | null(null); useEffect(() { const controller new AbortController(); fetch(/api/experiment-data, { signal: controller.signal }) .then((response) response.json()) .then(setData); return () controller.abort(); }, []); return data ? ExperimentView data{data} modeclient / : p页面壳体已经到达内容准备中。/p; }实验使用生产构建不测开发服务器。每种策略运行 5 次冷导航每次使用独立浏览器上下文取中位数。LCP 通过页面可见性仿真取得因此只在同一批次、同一浏览器条件内比较。结果三个“快”不是一回事指标静态生成请求时渲染客户端渲染TTFB7 ms197.2 ms10.7 msLCP120 ms288 ms108 ms完整内容就绪18.7 ms205.7 ms364.1 ms导航传输5,287 B6,925 B3,129 B客户端脚本224,110 B224,110 B224,110 B数据接口请求001这组数字最有价值的地方不是谁赢了而是它暴露了三条不同的等待链。1. 请求时渲染把数据等待放进了 TTFB动态路线的固定数据延迟是 180 ms中位 TTFB 为 197.2 ms。服务器必须先拿到数据才能返回完整响应。这不表示 SSR 一定慢。它表示当完整页面依赖请求时数据时数据源延迟、服务器渲染和排队成本会成为 TTFB 的一部分。换成更快的数据源、可共享缓存或流式边界结果都会改变。2. 客户端壳体早到不代表完整内容早到客户端路线 TTFB 只有 10.7 msLCP 甚至是三者中最低的 108 ms但完整内容到 364.1 ms 才准备好。原因不是“LCP 无用”而是 LCP 只记录视口中最大的内容绘制。这个页面的早期壳体已经产生了候选元素后续业务表格没有形成更晚、更大的 LCP 候选。因此工具型页面不能只看 LCP。还需要记录与用户任务相关的指标例如核心内容就绪可操作时间首次有效数据搜索结果出现表单可以提交关键业务成功事件。3. 静态生成把同一份等待移到了发布阶段静态路线同样执行了 180 ms 数据等待但它发生在构建阶段不在用户请求链路中所以运行时 TTFB 和内容就绪都更早。这正是内容可预先确定时静态生成的优势但代价也很明确数据新鲜度、构建规模和失效机制要由发布系统承担。如果页面依赖用户权限、Cookie、实时库存或强个性化数据就不能为了这组实验数字强行静态化。4. 不要从架构名字推断 JavaScript 体积本轮三条路线的客户端脚本传输量完全相同都是 224,110 B。这是一个需要保留的反例客户端渲染经常会带来更多客户端工作但不能仅凭“CSR”三个字就断言本项目的脚本一定更大。组件边界、依赖和构建产物才是证据。真正可执行的选择顺序选择渲染策略时可以先问四个问题。第一问内容能否在请求前确定能优先静态生成能短暂陈旧考虑缓存与时间或事件驱动再验证不能进入请求时动态渲染。第二问是否依赖请求上下文Cookie、Header、权限、地域、A/B 分组和实时数据通常会限制共享缓存。需要先定义缓存键和隐私边界再决定是否动态渲染而不是先上 SSR 再补缓存。第三问慢数据能否缩小到一个组件边界如果只有推荐、评论或库存较慢可以保留静态或缓存壳体把慢片段放进 Suspense 流式返回。Next.js 16 的 Cache Components 就是围绕“静态、缓存和动态内容共存”设计的。不要因为页面里一个按钮需要状态就把整棵组件树都标成客户端组件也不要因为一个动态区块就让整页每次请求重新生成。第四问你实际要改善哪个指标TTFB 慢看数据源、服务器计算、缓存命中、边缘与回源LCP 慢继续拆 TTFB、资源发现、下载和渲染延迟INP 慢看客户端 JavaScript、长任务和渲染范围内容就绪慢看串行数据请求、hydration 和业务数据链服务成本高看请求时渲染次数、缓存复用和容量。一个改动必须能说明它改变了哪一段因果链。一份发布前检查表在把某个路由改成 SSR、静态或客户端渲染前至少确认完整内容是在构建、请求还是浏览器阶段生成页面与数据缓存是否分开定义了复用和失效个性化输出是否可能误入共享缓存慢数据是否能缩小到 Suspense 或组件边界Client Component 是否只覆盖真正需要交互的子树除 LCP 外是否记录了核心内容就绪或业务成功指标实验室数据是否与真实用户监控分开解释发布后能否按路由、设备、网络和版本观察回归。SSR 的价值是真实存在的它可以让关键 HTML 更早出现、改善无 JavaScript 时的可读性并帮助部分抓取器获取内容。但它同时可能增加 TTFB、服务器容量和 hydration 成本。更可靠的结论不是“SSR 更快”或“CSR 更快”而是先确定内容在哪个阶段生成再测量等待落在哪条链路上渲染策略是手段用户任务完成时间才是结果。事实、验证与边界说明官方事实Next.js App Router 的页面和布局默认是 Server Components交互与浏览器 API 需要 Client Componentsconnection()可使后续渲染等待请求并排除预渲染Cache Components 可以在同一路由组合静态、缓存与动态内容。Core Web Vitals 当前稳定指标为 LCP、INP、CLS实验室数据不能替代真实用户数据。个人已实践本文三条路线、生产构建、15 个隔离浏览器上下文、5 次冷导航中位数、构建分类和页面行为均已在本地实验中执行并保留原始证据。工程归纳四问决策链、指标因果拆解和检查表是基于官方文档与本地实验形成的工程建议不是 Next.js 官方固定架构。未验证推断本文没有公网 CDN、真实用户设备、搜索索引、服务器容量或生产成本数据不声称任何渲染策略会在所有项目中固定领先。版本边界代码与结论以 Next.js 16.2.12 实验和 2026-07-28 可访问的官方文档为基线使用其他版本时应重新核对缓存与渲染语义。官方参考Next.jsServer and Client ComponentsNext.jsconnectionNext.jsCache ComponentsNext.jsRevalidatingweb.devWeb Vitals