跳到主要内容

SSR 与 SSG

问题

什么是 CSR、SSR、SSG、ISR?它们分别解决什么问题?真实项目里应该怎么选?面试时如何把渲染流程、性能指标、Hydration、缓存和常见坑讲清楚?

面试速答版

什么是 CSR、SSR、SSG、ISR? 四种渲染方式区别在于「HTML 在哪生成、什么时候生成」:

  • CSR:浏览器拿到空 HTML 壳,下载 JS 后才渲染(Vue/React 默认)。
  • SSR:每次请求时服务器实时渲染出完整 HTML(getServerSideProps、Next.js dynamic)。
  • SSG:构建时一次性把所有页面预渲染成静态 HTML,部署到 CDN(getStaticProps、博客/文档站)。
  • ISR:SSG 的升级版,构建时静态化 + 运行时按 revalidate 时间增量更新(Next.js 的 revalidate: 60)。

它们分别解决什么问题? 核心是在「首屏快、SEO、数据新鲜度、服务器成本」之间权衡:

  • CSR 解决「前后端分离 + 服务器零成本」,但牺牲首屏和 SEO。
  • SSR 解决「首屏白屏 + SEO + 个性化数据」,但 TTFB 升高、服务器压力大。
  • SSG 解决「极致首屏 + 极低成本」,但内容更新要重新构建。
  • ISR 是 SSG 痛点的解药——既享受静态化的速度,又能让数据按需更新。

真实项目里应该怎么选? 按场景套口诀就行:

  • 要实时数据用 SSR:商品详情、个性化首页、登录后的内容。
  • 内容稳定用 SSG:博客、文档、营销页、官网。
  • 页面多 + 要更新用 ISR:电商品类页、新闻列表、CMS 驱动的内容。
  • 强交互后台用 CSR:管理后台、低代码平台、在线编辑器。
  • 其实 Next.js App Router 现在能混用——一个页面里 Server Components 走 SSR/SSG,互动部分用 Client Components 走 CSR。

面试时如何把渲染流程、性能指标、Hydration、缓存和常见坑讲清楚? 按这个顺序展开就抓得住要点:

  • 渲染流程:画时序图——浏览器请求 → 服务端取数 + 渲染 → 返回 HTML → 浏览器展示 → 下载 JS → Hydration 让页面可交互。
  • 性能指标:SSR/SSG 改善 TTFB(SSG 更好)/FCP/LCP,但 TTI/INP 仍取决于 JS 大小和 Hydration 成本。
  • Hydration:服务端 HTML 和客户端首次渲染必须完全一致,否则报 hydration mismatch。常见坑是用了 Date.now()/Math.random()/window
  • 缓存:SSR 用 CDN 边缘缓存 + Cache-Control、ISR 用增量重生成、SSG 直接 CDN 强缓存。
  • window is not defined(用 useEffecttypeof window 判断)、敏感数据泄漏到 props、并行请求避免串行 waterfall。

一句话概括

CSR 是浏览器拿到空壳后自己渲染,SSR 是每次请求时服务器现做 HTML,SSG 是构建时提前做好 HTML,ISR 是先静态化,再按规则增量更新。

记住一句面试口诀:

要实时,用 SSR;内容稳定,用 SSG;页面很多又要更新,用 ISR;强交互后台系统,用 CSR。

先抓住本质

渲染方案讨论的不是“React/Vue 在哪里跑”这么简单,而是在权衡四件事:

  • 首屏快不快:用户多久能看到内容
  • 能不能 SEO:爬虫拿到的是内容还是空壳
  • 数据新不新:页面是不是每次请求都拿最新数据
  • 成本高不高:服务器、构建、缓存、复杂度谁来承担

四种渲染方式总览

方式HTML 什么时候生成首屏内容SEO数据新鲜度服务器压力典型场景
CSR浏览器运行时慢,先空壳取决于接口后台系统、强交互应用
SSR每次请求时快,有内容最新搜索页、个性化页、实时价格
SSG构建时最快,有内容构建时数据极低文档、博客、营销页
ISR构建时 + 运行时增量快,有内容可接受延迟商品详情、新闻、CMS 页面

更短的记法:

方案像什么
CSR到店后现点现做,还要自己等厨师开火
SSR每来一桌客人,后厨现做一份
SSG开店前把套餐都做好,客人来了直接端
ISR先做好套餐,过一段时间发现旧了再后台换新

先分清几个容易混淆的指标

面试里最容易把“首屏快”“可交互快”“接口快”混成一团。渲染方案主要影响这些指标:

指标含义和渲染方式的关系
TTFB浏览器收到第一个字节的时间SSR 往往更高,因为服务端要取数和渲染
FCP页面首次绘制内容的时间SSR/SSG 通常优于 CSR
LCP最大内容块出现的时间SSR/SSG 有利于正文、标题、首图尽早出现
TTI页面真正可交互的时间SSR/SSG 不一定快,仍然要下载 JS 并 Hydration
INP用户交互响应延迟JS 体积、Hydration、长任务都会影响

关键结论
SSR/SSG 解决的是“更早看到内容”和“SEO”,但页面能不能点,还要看客户端 JS 下载、执行和 Hydration。


CSR:客户端渲染

CSR 的流程是:服务器先给一个很薄的 HTML,浏览器下载 JS,JS 执行后再请求数据,最后渲染页面。

<!-- CSR 初始 HTML 通常只有一个挂载点 -->
<div id="root"></div>
<script src="/assets/app.js"></script>

CSR 的优缺点

优点缺点
部署简单,静态资源即可托管首屏容易白屏
服务器压力小SEO 天然较差
前后端分离清晰低端设备执行 JS 慢
适合复杂交互和登录后系统首屏依赖 JS、接口、网络三件事

CSR 适合什么

  • 后台管理系统
  • 登录后才能看的用户中心
  • 在线编辑器、低代码平台、地图类应用
  • SEO 不重要、交互比首屏内容更重要的页面

SSR:服务端渲染

SSR 的流程是:浏览器请求页面,服务器取数据,把组件渲染成 HTML 返回。浏览器先展示 HTML,然后再下载 JS,执行 Hydration,让页面可交互。

SSR 解决了什么

SSR 最核心解决两个问题:

  1. 首屏白屏问题:浏览器不必等完整 JS 执行完才看到内容。
  2. SEO 问题:爬虫能直接拿到带内容的 HTML。

但 SSR 不等于“所有性能都更好”。它把一部分成本从浏览器挪到了服务器:

Next.js Pages Router 示例

// pages/products/[id].tsx
import type { GetServerSideProps } from 'next';

interface Product {
id: string;
name: string;
price: number;
}

export default function ProductPage({ product }: { product: Product }) {
return (
<main>
<h1>{product.name}</h1>
<p>价格:¥{product.price}</p>
</main>
);
}

export const getServerSideProps: GetServerSideProps = async ({ params }) => {
const response = await fetch(`https://api.example.com/products/${params!.id}`);

if (response.status === 404) {
return { notFound: true };
}

const product = await response.json();

return {
props: { product },
};
};

Next.js App Router 示例

// app/products/[id]/page.tsx
export const dynamic = 'force-dynamic';

export default async function ProductPage({
params,
}: {
params: Promise<{ id: string }>;
}) {
const { id } = await params;
const product = await fetch(`https://api.example.com/products/${id}`, {
cache: 'no-store',
}).then(res => res.json());

return (
<main>
<h1>{product.name}</h1>
<p>价格:¥{product.price}</p>
</main>
);
}

SSR 的优缺点

优点缺点
首屏有内容,FCP/LCP 通常更好TTFB 可能更高
SEO 和社交分享友好每次请求都可能消耗服务器资源
适合实时、个性化数据缓存设计更复杂
可以在服务端安全读取 cookie、鉴权Hydration 成本仍然存在

SSR 常见坑

原因解决方式
window is not defined服务端没有浏览器对象放进 useEffect,或判断 typeof window !== 'undefined'
Hydration mismatch服务端 HTML 和客户端首次渲染结果不一致避免首屏直接用随机数、当前时间、浏览器环境差异
TTFB 变慢服务端串行请求、接口慢、渲染重并行请求、缓存、Streaming、拆分慢组件
服务器压力大每个请求都渲染CDN、页面缓存、接口缓存、边缘渲染
泄露敏感数据把 token、内部字段塞进 props只返回客户端必要字段

SSG:静态站点生成

SSG 的流程是:构建时就把页面生成好,部署成 HTML、CSS、JS 等静态文件。用户访问时,CDN 直接返回 HTML。

SSG 为什么快

因为它把“取数据 + 渲染 HTML”的成本提前到了构建阶段。线上请求时不需要临时计算,只要读静态文件。

Next.js Pages Router 示例

// pages/posts/[slug].tsx
import type { GetStaticPaths, GetStaticProps } from 'next';

interface Post {
slug: string;
title: string;
content: string;
}

export default function PostPage({ post }: { post: Post }) {
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}

export const getStaticPaths: GetStaticPaths = async () => {
const posts = await getAllPosts();

return {
paths: posts.map(post => ({
params: { slug: post.slug },
})),
fallback: false,
};
};

export const getStaticProps: GetStaticProps = async ({ params }) => {
const post = await getPostBySlug(params!.slug as string);

return {
props: { post },
};
};

Next.js App Router 示例

// app/posts/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getAllPosts();

return posts.map(post => ({
slug: post.slug,
}));
}

export default async function PostPage({
params,
}: {
params: Promise<{ slug: string }>;
}) {
const { slug } = await params;
const post = await getPostBySlug(slug);

return (
<article>
<h1>{post.title}</h1>
<div>{post.content}</div>
</article>
);
}

SSG 的优缺点

优点缺点
首屏非常快,TTFB 很低内容更新通常要重新构建
CDN 分发,高并发能力强页面数量巨大时构建慢
服务器成本低,可用性高不适合强实时、强个性化页面
SEO 友好构建期数据源不可用会影响发布

SSG 适合什么

  • 文档站
  • 博客
  • 官网、营销页、活动页
  • 内容更新频率低的 CMS 页面
  • 商品详情中“允许分钟级延迟”的部分

ISR:增量静态再生

ISR 可以理解为:先按 SSG 返回静态页面,过期后触发后台重新生成。

它解决的是 SSG 的两个痛点:

  1. 页面太多,全部构建太慢。
  2. 内容会更新,但又不值得每次请求都 SSR。

ISR 的核心细节

细节解释
不是到点自动更新通常是过期后的下一次请求触发再生成
过期后先返回旧页面用户不会因为再生成而一直等待,除非是首次 fallback blocking
再生成失败怎么办一般继续保留旧页面,避免线上直接坏掉
数据不是绝对实时它追求的是“可接受延迟 + 静态性能”

Next.js Pages Router 示例

export const getStaticProps: GetStaticProps = async ({ params }) => {
const product = await getProduct(params!.id as string);

return {
props: { product },
revalidate: 60,
};
};

export const getStaticPaths: GetStaticPaths = async () => {
const popularProducts = await getPopularProducts();

return {
paths: popularProducts.map(product => ({
params: { id: product.id },
})),
fallback: 'blocking',
};
};

fallback 三种模式

行为适合场景
false未预生成路径直接 404路径有限、必须提前确定
true先返回 fallback 页面,再客户端补齐能接受骨架屏,页面体验要自己处理
'blocking'首次请求等待生成完成更像 SSR,用户不会看到半成品
function ProductPage({ product }) {
const router = useRouter();

if (router.isFallback) {
return <ProductSkeleton />;
}

return <ProductDetail product={product} />;
}

On-Demand ISR

定时 revalidate 适合“最多旧 60 秒”这种场景。CMS 发布文章、商品改价这类事件,更适合按需触发。

// pages/api/revalidate.ts
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.query.secret !== process.env.REVALIDATE_SECRET) {
return res.status(401).json({ message: 'Invalid token' });
}

try {
await res.revalidate(req.body.path);
return res.json({ revalidated: true });
} catch {
return res.status(500).json({ message: 'Revalidate failed' });
}
}

Next.js App Router 里的再验证

// 方式一:整页或 fetch 缓存按时间再验证
export const revalidate = 60;

export default async function Page() {
const product = await fetch('https://api.example.com/product/1', {
next: { revalidate: 60 },
}).then(res => res.json());

return <ProductDetail product={product} />;
}
// 方式二:按 tag 失效,适合 CMS Webhook
import { revalidateTag } from 'next/cache';

export async function POST() {
revalidateTag('products');

return Response.json({ ok: true });
}

Hydration:SSR/SSG 后为什么还要“激活”

SSR/SSG 返回的是 HTML,HTML 只能让用户“看到内容”。如果按钮要点击、输入框要响应、组件要有状态,客户端还需要下载 JS,把事件绑定回去,这个过程叫 Hydration。

Hydration 的本质

React 不会把 SSR 生成的 DOM 全部删掉重建,而是:

  1. 在客户端重新跑一遍组件逻辑,生成虚拟树。
  2. 尝试和已有 HTML 对齐。
  3. 给对应 DOM 绑定事件。

所以 Hydration 最怕的就是:服务端渲染结果和客户端第一次渲染结果不一样。

Hydration mismatch 常见来源

来源示例解决方式
时间new Date().toLocaleString()服务端传固定值,或客户端挂载后再显示
随机数Math.random()服务端生成并传入,或使用稳定 id
浏览器 APIwindow.innerWidthuseEffect 中读取
用户本地状态localStorage.theme先渲染稳定默认值,挂载后更新
环境差异服务端和客户端 locale 不一致明确指定 locale/timeZone
非法 HTMLp 里嵌 div修正 HTML 结构
// 不推荐:首屏服务端和客户端可能不一致
export function Time() {
return <span>{new Date().toLocaleString()}</span>;
}

// 推荐:先保持一致,挂载后再展示客户端时间
export function ClientTime() {
const [time, setTime] = useState('');

useEffect(() => {
setTime(new Date().toLocaleString());
}, []);

return <span>{time}</span>;
}

SSR 快,但 Hydration 可能拖后腿

SSR 页面可能出现这种情况:

这就是为什么 SSR 项目也要关心 JS 体积、代码分割、组件复杂度和长任务。


Streaming SSR 与选择性 Hydration

传统 SSR 常见问题是:服务器必须等所有数据都准备好,才能返回完整 HTML。只要一个慢接口卡住,整个页面都晚到。

Streaming SSR 的思路是:先把能渲染的部分流式发给浏览器,慢的部分用 fallback 占位,数据到了再继续推送。

import { Suspense } from 'react';

export default function Page() {
return (
<main>
<Header />
<Suspense fallback={<ProductSkeleton />}>
<Product />
</Suspense>
<Suspense fallback={<ReviewsSkeleton />}>
<Reviews />
</Suspense>
</main>
);
}

React Server Components 和 Hydration 的关系

React Server Components(RSC)不是“传统 SSR 的同义词”。它的重点是:组件只在服务端执行,结果发给客户端,但组件代码本身不进客户端 bundle,也不需要 Hydration。

类型在哪里执行会进客户端 JS 吗能用状态和事件吗
Server Component服务端不会不能用 useStateonClick
Client Component服务端预渲染 + 客户端 Hydration可以
// Server Component:适合取数据、读文件、访问数据库
export default async function ProductInfo() {
const product = await db.product.findUnique({ where: { id: '1' } });

return <h1>{product.name}</h1>;
}
// Client Component:适合交互
'use client';

export function AddToCartButton() {
const [count, setCount] = useState(0);

return <button onClick={() => setCount(count + 1)}>加入购物车</button>;
}

面试回答时可以这样说:

SSR 让 HTML 更早出现,RSC 进一步减少发到客户端的 JS;真正需要交互的部分才作为 Client Component Hydration。


缓存:SSR/SSG/ISR 的灵魂

渲染策略和缓存是绑在一起的。只讲 SSR/SSG,不讲缓存,面试官很容易继续追问。

常见缓存层

缓存层缓存什么常见用途
CDN 缓存HTML、JS、CSS、图片SSG/ISR 静态资源分发
页面缓存渲染后的 HTML/RSC payload减少 SSR 重复渲染
数据缓存接口结果、数据库查询结果减少慢接口和 DB 压力
浏览器缓存静态资源减少重复下载

SSR 页面能不能缓存

可以,但要看页面是否个性化。

页面类型能否缓存说明
全站一样的页面可以强缓存或 CDN 缓存更接近 SSG/ISR
按地区不同可以按地区维度缓存key 要包含地区
按登录用户不同谨慎缓存key 必须包含用户维度,防止串数据
包含隐私信息通常不缓存 HTML可以缓存公共接口数据

缓存最常见事故

事故原因
A 用户看到 B 用户信息缓存 key 没带用户标识
商品价格迟迟不更新CDN、页面、接口多层缓存未统一失效
页面更新了但接口没更新只 revalidate 页面,没处理数据缓存
构建后页面还是旧的构建使用了旧数据源或缓存未清

真实项目怎么选

先不要背“SSR 好还是 SSG 好”,要按页面维度选。一个站点里通常会混用多种方式。

常见业务选择

场景推荐原因
文档站SSG内容稳定,CDN 极快
博客文章SSG/ISRSEO 重要,更新不频繁
新闻详情ISR/SSR看实时性要求
新闻首页SSR/ISR更新频繁,SEO 重要
商品详情页ISR页面多,允许短暂延迟
商品价格/库存SSR 或客户端实时请求数据敏感且变化快
搜索结果页SSRquery 多、内容动态、SEO 可能重要
用户订单页CSR/SSR登录态强,SEO 不重要
后台管理系统CSR强交互,SEO 不重要
活动页SSG静态资源和首屏体验优先

一个电商站的混合方案

面试时可以补一句:

渲染策略不是项目级选择,而是页面级甚至组件级选择。公共内容可以静态化,个性化和实时内容再动态获取。


面试高频追问

Q1:SSR 和 SSG 最大区别是什么?

答案:渲染时机不同。

对比SSRSSG
渲染时机每次请求时构建时
数据新鲜度最新构建时确定
TTFB取决于服务端处理速度通常很低
服务器压力
适用场景动态、个性化、实时页面静态、稳定、SEO 页面

一句话版:

SSR 是用户来了现做,SSG 是上线前做好。

Q2:SSR 一定比 CSR 快吗?

答案:不一定,要看“快”的定义。

  • 如果说“更早看到内容”,SSR 通常更快。
  • 如果说“页面可交互”,SSR 不一定更快,因为还要下载 JS 和 Hydration。
  • 如果 SSR 接口很慢、服务器压力大,TTFB 甚至可能比 CSR 更差。

标准回答:

SSR 优化的是首屏内容和 SEO,不天然保证 TTI/INP。SSR 页面仍然要控制 JS 体积、减少 Hydration 成本、避免长任务。

Q3:SSR 为什么 SEO 更好?

CSR 初始 HTML 里通常只有一个空的 root,主要内容要等 JS 执行后才出现。SSR 返回的 HTML 已经包含标题、正文、结构化内容,搜索引擎和社交平台抓取更稳定。

<!-- CSR 爬虫可能先看到这个 -->
<div id="root"></div>

<!-- SSR/SSG 爬虫能直接看到这个 -->
<main>
<h1>商品标题</h1>
<p>商品描述</p>
</main>

Q4:什么是 Hydration?

Hydration 是客户端 JS 接管服务端 HTML 的过程。它会在客户端重新执行组件逻辑,和已有 DOM 对齐,然后绑定事件,让静态 HTML 变成可交互页面。

面试关键点:

  • SSR/SSG 返回 HTML,只负责“可见”
  • Hydration 后页面才“可交互”
  • 服务端和客户端首次渲染不一致会导致 mismatch
  • Hydration 成本过高会影响 TTI/INP

Q5:Hydration mismatch 怎么排查?

优先看这些:

  1. 首屏是否用了 Date.now()new Date()Math.random()
  2. 是否在 render 阶段读取 windowdocumentlocalStorage
  3. 服务端和客户端的语言、时区、环境变量是否不同。
  4. HTML 结构是否合法。
  5. 是否有组件根据屏幕宽度在首屏渲染不同结构。

解决思路:

  • 保证首屏输出稳定
  • 浏览器专属逻辑放到 useEffect
  • 服务端生成的数据通过 props 传给客户端
  • 对不可避免的差异使用客户端挂载后再渲染

Q6:ISR 和 SSR 有什么区别?

对比ISRSSR
返回内容多数时候返回缓存静态页每次请求生成
数据实时性有延迟更实时
性能接近 SSG取决于服务器和接口
成本中等较高
适合可接受短暂旧数据的页面必须实时或个性化页面

一句话版:

ISR 是“旧页面先顶上,后台慢慢换新”;SSR 是“每次都现场生成”。

Q7:ISR 到了 revalidate 时间会自动重新构建吗?

通常不是。更准确地说:

  1. revalidate 时间内,直接返回缓存页面。
  2. 超过时间后,下一次请求仍可能先拿到旧页面。
  3. 框架在后台触发重新生成。
  4. 生成成功后,后续请求拿到新页面。
  5. 生成失败则继续保留旧页面。

所以 ISR 不是严格实时更新,而是“过期后按请求触发更新”。

Q8:SSR 如何做性能优化?

常见方向:

  • 并行数据请求:避免接口瀑布流。
  • 缓存接口数据:慢数据不要每次都查库。
  • 缓存页面结果:对非个性化 SSR 页面做 CDN/页面缓存。
  • Streaming SSR:慢模块用 Suspense 拆开,不阻塞整页。
  • 减少客户端 JS:代码分割、RSC、懒加载重组件。
  • 边缘渲染:把渲染放到离用户更近的位置。
  • 监控 TTFB/LCP/INP:别只看服务端日志。
// 避免串行
const user = await getUser();
const posts = await getPosts();

// 推荐并行
const [user, posts] = await Promise.all([
getUser(),
getPosts(),
]);

Q9:SSR 有哪些安全风险?

风险说明
敏感数据泄露服务端 props 被序列化到 HTML 里,客户端能看到
缓存串用户CDN 或页面缓存没有区分 cookie/user
XSS服务端拼接 HTML 或使用 dangerouslySetInnerHTML
内部接口暴露把内部错误、token、debug 信息返回到页面

最重要的一句:

服务端能拿到的数据,不代表都能传给客户端。

Q10:Next.js App Router 里怎么判断静态还是动态?

粗略理解:

  • 没有动态请求信息、没有禁用缓存的数据请求,更容易静态化。
  • 使用 cookies()headers()searchParamscache: 'no-store'dynamic = 'force-dynamic' 等,通常会让页面变动态。
  • 使用 revalidatefetch(..., { next: { revalidate } }) 可以做类似 ISR 的缓存再验证。
// 静态或可再验证
export const revalidate = 3600;

// 强制动态
export const dynamic = 'force-dynamic';

// 单个请求不缓存
await fetch(url, { cache: 'no-store' });

Q11:为什么 SSG 页面很多时会有问题?

因为所有静态路径都要在构建阶段生成。页面数量从 1 千涨到 100 万时,构建时间、构建内存、数据源压力、部署产物体积都会变成问题。

解决方式:

  • 热门页面预生成
  • 长尾页面用 fallback/按需生成
  • 用 ISR 分批更新
  • 拆分站点或拆分构建任务
  • 对内容源做缓存和限流

Q12:服务端渲染和同构渲染是一回事吗?

不完全是。

  • 服务端渲染:强调 HTML 在服务端生成。
  • 同构渲染:强调同一套组件代码既能在服务端渲染,也能在客户端运行并接管。

React/Vue 的 SSR 通常都是同构渲染:服务端先渲染 HTML,客户端再 Hydration。


最后用一张表记住

你听到的需求第一反应
“SEO 很重要”SSR / SSG / ISR
“必须实时展示最新数据”SSR
“内容基本不变”SSG
“页面很多,内容偶尔更新”ISR
“登录后后台系统”CSR
“首屏要快,但按钮也要尽快能点”SSR/SSG + 减少 JS + 优化 Hydration
“爬虫和社交分享要稳定”SSR/SSG
“不能让用户看到旧价格”SSR 或客户端实时请求关键字段
“访问量巨大,内容可缓存”SSG/ISR + CDN

面试回答模板

可以按这个顺序说,基本能覆盖大多数追问:

  1. 先定义:CSR 浏览器渲染,SSR 请求时渲染,SSG 构建时渲染,ISR 静态页过期后增量更新。
  2. 讲流程:SSR 返回 HTML 后还要 Hydration,SSG/ISR 主要靠 CDN 返回静态 HTML。
  3. 讲取舍:SSR 数据新但服务器压力大,SSG 快但更新慢,ISR 折中,CSR 适合强交互后台。
  4. 讲指标:SSR/SSG 改善 FCP/LCP/SEO,但 TTI/INP 还受 JS 和 Hydration 影响。
  5. 讲实践:按页面选型,公共内容静态化,个性化/实时内容动态化,缓存 key 和失效策略要设计好。

相关链接