题记与说明
- 日期:2026-11-01
- 类型:主题研究(先检索后精读,共精读 6 个核心来源 + 1 个补充来源,网络可用)
- 条目前缀:knowledge-4okgaw4b-0
- 来源方式说明:本条目为"主题"而非单篇文献,故 source 记为主题本身;下列每篇来源均保留原始 URL 与抓取方式。
一、来源清单
| # | 来源 | 发布/更新时间 | 角色 | 抓取方式 |
|---|---|---|---|---|
| 1 | Web Vitals (web.dev) | 2020-05 发布,2024-10 更新 | CWV 指标框架 | web_reader 全文 |
| 2 | Optimize Largest Contentful Paint (web.dev) | 2020-04 发布,2025-03 更新 | LCP 优化方法论 | web_reader 全文 |
| 3 | HTTP caching (MDN) | 2026-08 更新 | HTTP 缓存权威指南 | web_reader 全文 |
| 4 | Image performance (web.dev Learn) | 2023-11 更新 | 图片优化 | web_reader 全文 |
| 5 | Reduce JavaScript payloads with code splitting (web.dev) | 2018-11 | 代码分割 | web_reader 全文 |
| 6 | Customize headers — Cloudflare Pages docs | 在线文档 | 静态站点部署(缓存头配置) | WebFetch 摘要 |
| 7(补充) | Optimize CLS (web.dev) | 在线更新 | 视觉稳定性 | WebFetch 摘要 |
检索过程:先用 WebSearch 定位了 web.dev / MDN / Cloudflare 等官方来源(检索词含 "Core Web Vitals LCP INP CLS thresholds"、"MDN HTTP caching Cache-Control ETag"、"web.dev image optimization AVIF WebP"、"reduce JavaScript payloads code splitting"、"Netlify OR Vercel OR Cloudflare Pages docs cache control"),再对命中页面逐篇精读。
二、各来源精读笔记
1. Web Vitals:CWV 指标框架(web.dev)
定位:Google 发起的"统一体验质量信号"计划,把过去泛滥的性能工具/指标收敛为三个核心指标。
"Site owners shouldn't have to be performance experts to understand the quality of experience they are delivering to their users."
当前三指标(阈值按 75 分位衡量,移动/桌面分开):
- LCP(Largest Contentful Paint,加载):≤ 2.5 s 为好,> 4.0 s 为差。
- INP(Interaction to Next Paint,交互):≤ 200 ms 为好,> 500 ms 为差。(INP 于 2024 年 3 月正式替代 FID。)
- CLS(Cumulative Layout Shift,视觉稳定性):≤ 0.1 为好,> 0.25 为差。
"The current set focuses on three aspects of the user experience—*loading*, *interactivity*, and *visual stability*."
"a good threshold to measure is the 75th percentile of page loads, segmented across mobile and desktop devices."
指标生命周期:experimental → pending → stable,稳定指标"一年最多改一次"("Stable Core Web Vitals metrics won't change more than once per year"),并有公开 CHANGELOG。
测量方式:
- Field 数据:CrUX(Chrome 真实用户匿名数据)驱动 PageSpeed Insights、Search Console、DevTools;官方强调 CrUX 只是快捷方式,仍应自建 RUM:"we strongly recommend that sites set up their own real-user monitoring."
- JS 采集:
web-vitals库几行代码即可上报:
import {onCLS, onINP, onLCP} from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify(metric);
(navigator.sendBeacon && navigator.sendBeacon('/analytics', body)) ||
fetch('/analytics', {body, method: 'POST', keepalive: true});
}
onCLS(sendToAnalytics); onINP(sendToAnalytics); onLCP(sendToAnalytics);- Lab 数据不可替代 field:"Only field measurement can accurately capture the complete picture."(设备能力、网络、其他进程、用户交互都会影响分数。)
辅助指标:TTFB 与 FCP 用于诊断 LCP(慢服务器 vs 渲染阻塞资源);TBT 是 lab 指标,用于诊断 INP,但"not field-measurable"故不入核心集。
2. Optimize LCP:把 LCP 拆成四段逐一优化(web.dev)
核心方法论:LCP 不能"一把抓",要拆成无重叠、无间隙、相加恒等于总时长的四个子段:
| 子段 | 含义 | 理想占比 |
|---|---|---|
| TTFB | 用户发起导航到收到 HTML 首字节 | ~40% |
| Resource load delay | TTFB → 开始加载 LCP 资源 | <10% |
| Resource load duration | 加载 LCP 资源本身 | ~40% |
| Element render delay | LCP 资源加载完 → 元素完全渲染 | <10% |
"Every single page can have its LCP value broken down into these four subparts. There is no overlap or gap between them."
理想状态:"The __vast majority__ of the LCP time should be spent loading the HTML document and LCP source. Any time before LCP where one of these two resources is *not* loading is __an opportunity to improve__."
关键警告(防"伪优化"):只压缩图片可能不降 LCP,省下的时间会转移给 render delay(如果 LCP 元素要等 JS 揭示)——"it wouldn't actually improve LCP because the time would just shift to the *element render delay* subpart"。所以四段都要优化。
逐段处方(按影响排序):
- 消灭 resource load delay:
- 判断标准:"your LCP resource should start loading at the same time as the first resource loaded by that page"。
- LCP 资源必须在初始 HTML 中可被 preload scanner 发现;只在 CSS/JS 里引用的,用
<link rel="preload" fetchpriority="high" as="image">补救。 - 绝不能懒加载 LCP 图:"Never lazy-load your LCP image, as that will always lead to unnecessary resource load delay, and will have a negative impact on LCP."
- 给 LCP 图加
fetchpriority="high"(但"setting a high priority on more than one or two images makes priority setting unhelpful")。 - 关键资源尽量与 HTML 同源(跨源要新建连接)。
- 消灭 element render delay:减小/内联渲染阻塞 CSS("inlining your style sheet is only recommended if your style sheet is small");
<head>里几乎永远不该放同步脚本;用 SSR——SSG/预渲染更好:"If prerendering is possible with your architecture, it's generally a better choice for performance.";打断长任务(图片渲染也在主线程)。 - 缩短 resource load duration:减尺寸(最优尺寸/现代格式/压缩)、减距离(CDN,图片 CDN 尤佳)、减带宽竞争、用高效缓存策略把网络时间归零("bringing the *resource load duration* to essentially zero")。
- 官方提醒:"the load duration portion of LCP tends not to be a significant bottleneck for most sites"——先看真实数据再下力气。
- 降低 TTFB:减少重定向链、避免带唯一查询参数导致 CDN 边缘缓存失效("all requests must be directed all the way back to the origin server")。
诊断入口:PageSpeed Insights 优先看 CrUX 分 URL→分 origin→分设备的四类 field 数据;TTFB 与 FCP 差值大 = 渲染阻塞多/CSR 过重;FCP 与 LCP 差值大 = LCP 资源发现晚或主线程被占。
3. HTTP Caching(MDN):缓存模型与两种通用模式
缓存分型:
- private cache(浏览器本地):可存个性化响应;个性化内容必须
Cache-Control: private,"the presence of a cookie does not always indicate that it is private"。 - shared cache:proxy cache(HTTPS 普及后基本只能隧道,少担忧)与 managed cache(CDN、反向代理、Service Worker + Cache API)——managed cache 可通过 API/dashboard 主动清除,"That allows for a more proactive caching strategy"。
核心机制:
- Heuristic caching:无
Cache-Control时也会缓存,规范建议复用约 (now − Last-Modified) 的 10%;"basically all responses should explicitly specify aCache-Controlheader."(所以"默认不写头"≠"不缓存"。) - fresh/stale 由 age 决定:
max-age是首选(HTTP/1.1),Expires已不必再提供;共享缓存会带Age头折算剩余新鲜度。 - Vary:按 URL + 指定请求头组合缓存键;不要用
Vary: User-Agent(变体过多大幅降低缓存命中)。 - 验证(revalidation):过期不丢弃,发条件请求
If-Modified-Since(配Last-Modified)或If-None-Match(配ETag),未变则304 Not Modified(无 body,"the transfer size is extremely small")。RFC9110 建议两者都发;同时存在时If-None-Match优先。 - no-cache ≠ no-store:"The
no-cachedirective does not prevent the storing of responses but instead prevents the reuse of responses without revalidation.";no-store才是禁止存储,但滥用no-store会失去 bfcache 等平台能力——"prefer the use ofno-cachein combination withprivate." - immutable:
Cache-Control: max-age=31536000, immutable让浏览器 reload 时不重新验证子资源。 - 无法远程删除:"There is no way to delete responses on an intermediate server that have been stored with a long
max-age." → 要么靠 cache busting 换 URL,要么用 managed cache 的 purge。
两种通用模式(MDN 给出的"绝大多数网站"配方):
- Cache busting 子资源:内容变则 URL 变(
bundle.v123.js/bundle.js?v=123/ 文件名哈希),配最长缓存:
# bundle.v123.js
Cache-Control: public, max-age=31536000, immutable
Last-Modified: Tue, 22 Feb 2022 20:20:20 GMT
ETag: "YsAIAAAA-QG4G6kCMAMBAAAAAAAoK"
public 仅在带 Authorization 头时才需要显式给出。QPACK 常用值表(HTTP/3 下 1 字节压缩):max-age=604800(1 周)/2592000(1 月)/public, max-age=31536000(1 年)。
- 主资源(HTML):URL 无法 cache busting,用验证式缓存:
Cache-Control: no-cache
Last-Modified: ...
ETag: "..."
个性化 HTML 加 private。favicon、manifest、.well-known、不可换 URL 的 API 同此法。
部署侧联动:标准 HTTP 缓存无法主动删除中间缓存内容;用允许 purge 的 CDN 或 Service Worker/Cache API 即可对主资源也做激进缓存,"storing the main resource and explicitly purging the relevant cache only when an update occurs"。
4. Image Performance(web.dev Learn):图片工程
前提:"Images are often the heaviest and most prevalent resource on the web."
四个优化杠杆:
- 正确的尺寸(dimensions):
- 目标: intrinsic size ≈ CSS 容器尺寸 × DPR。
- DPR 概念源自 iPhone 4(640×960 屏幕、DPR=2);"In most cases, the human eye is unable to benefit from a DPR of 3"——DPR≥3 可适当降档。
srcset(像素密度描述符1x/2x/3x或宽度描述符500w/1000w)+sizes(媒体条件 + CSS 像素显示宽度)由浏览器自动选图。- 注意:宽度描述符"must have both
sizesattribute and width descriptors, or they don't work"。
- 现代格式:
- "WebP is a widely supported format that works on all modern browsers"(有损/无损、带 alpha 的有损压缩)。
- "AVIF ... tests have shown greater than 50% savings when compared to JPEG in some cases",并支持 WCG/HDR;兼容性弱于 WebP。
- 用
<picture>做格式降级:AVIF → WebP → JPEG,<img>兜底且alt/width/height写在<img>上。
- 压缩:有损(量化 + 色度抽样,适合照片;锐边/文字类图慎用)与无损(GIF/PNG/WebP/AVIF);工具 Squoosh/ImageOptim/图片 CDN;"there isn't a universal setting suitable for all cases",需逐图试。
- 加载时序:
loading="lazy"视口内才下载(带宽让给关键资源);decoding属性控制解码时机(对超大高分辨率图才有可感知差异)。
复杂度管理(容易被忽视的反面教训):
"every additional image variant comes at a cost and makes less efficient use of the browser cache."
变体越多 → 缓存条目越多、回源概率越高、HTML 体积越大(每图可能多出数 KB)。平衡点是"合理的候选数量 + 实测用户体验";全宽 hero 图可以比商品缩略图多变体。
另一种方案:服务端按 Accept 请求头返回最优格式,并必须配 Vary 响应头让 CDN 正确分流,否则共享缓存会串格式。
5. Code Splitting(web.dev):JavaScript 体积工程
"Over 50% of users abandon a website if it takes longer than 3 seconds to load."
定义与收益链路:
"Code splitting is a technique that seeks to minimize startup time. When we ship less JavaScript at startup, we can get applications to be interactive faster by minimizing main thread work during this critical period."
- 对 INP:减少启动期 JS 的 parse/compile/execution 成本,释放主线程 → 输入响应更快。
- 对 LCP:在 CSR 架构下,markup 由 JS 渲染时,LCP 资源发现晚或主线程忙 → 减 JS 也能改善 LCP。
手段:
- 动态
import():按交互时机懒加载(文中示例:表单 submit 时才import('library.moduleA'));关键 chunk 可再 preload。 - vendor bundle 分离:第三方依赖"are split into a separate vendor bundle that can be cached since they don't update as often"(webpack SplitChunksPlugin)。
- 路由/组件级分割:靠框架抽象(React.lazy 等)比手写配置更简单。
- 配套技术:tree shaking(ES 模块静态分析去死代码,另一篇 web.dev 文章的姊妹技术)。
6. Optimize CLS(web.dev,补充来源)
- CLS 是无量纲分数(区别于基于时间的 LCP/INP):好 ≤ 0.1,差 > 0.25,目标仍是 75 分位;用户交互后 500 ms 内的位移不计入。
- 非预期位移的常见原因:图片/视频未设宽高、广告/嵌入/iframe 未留尺寸、动态注入内容、web 字体(FOUT/FOIT)、用
top/left等布局属性做动画、SPA 路由切换超过 500 ms 宽限。 - 处方:
<img width height>让浏览器在加载前按宽高比预留空间("This approach ensures that the browser can allocate the correct amount of space in the document while the image is loading.")+ CSSheight: auto;用aspect-ratio/min-height为广告嵌入预留槽位——"Ads are one of the largest contributors to layout shifts on the web.";晚到内容放视口下方或用骨架屏/overlay;字体用font-display: optional+ fallback 调优。
7. Customize headers(Cloudflare Pages docs):静态站点部署侧的缓存落地
机制:构建产物根目录放无扩展名纯文本 _headers 文件(框架项目放 public//static/),Pages 解析后对静态资产响应附加头;文件本身不会被当作静态资产发出;文件里定义的头覆盖 Cloudflare 默认头。
典型配方(文档原例——哈希资产长缓存):
/static/*
Cache-Control: public, max-age=31556952, immutable移除头:
/*.jpg
! Content-Security-Policy规则与坑(均出自该文档页):
- 自定义头不作用于 Pages Functions/SSR/_worker.js 生成的响应——那些要在函数代码里自己带头。
- splat(
*)贪婪匹配全部字符,一个 URL 只允许一个 splat;:name占位符可用一次。 - 重定向先于 headers 应用(redirect 命中则 headers 规则失效)。
- 多规则命中时头会被合并,重复值以逗号连接。
- 上限:100 条规则/文件,每行 2000 字符。
结合 MDN 的模式,即可得出静态站部署的标准策略:HTML → no-cache(+ ETag/Last-Modified),哈希子资源 → max-age=31536000, immutable,均由 _headers(Netlify/Vercel 同类机制)或平台配置声明。
三、七维提取(综合)
1. 核心问题
如何把"网页快不快"从主观感受变成可量化、可归因、可系统性优化、跨工具统一报告的工程问题。它需要同时回答三件事:用什么标准衡量(CWV)、瓶颈藏在链路哪一段(子段拆解)、用哪些杠杆去撬动(缓存/图片/JS/部署架构)。五份材料合起来是一条完整流水线:度量 → 归因 → 资源工程 → 缓存策略 → 部署架构。
2. 主要主张
- 统一度量:LCP/INP/CLS 三指标 + 75 分位 + field 数据优先,是当前判定"好体验"的公共语言(Google 全工具对齐)。
- 分段归因:任何单一 quick fix 都难显著改善 LCP;必须四段(TTFB/load delay/load duration/render delay)逐一优化,因为"时间会转移"而非消失。
- 缓存是最大杠杆:
cache busting + 长缓存 immutable(子资源)与no-cache + ETag 验证(HTML/主资源)两个模式即可覆盖绝大多数网站;别滥用no-store。 - 图片是最重资源,要做"尺寸工程":intrinsic size 对齐 容器×DPR,现代格式(AVIF/WebP)+ 压缩 + 懒加载,但绝不懒加载 LCP 图;变体数量要克制以免稀释浏览器缓存。
- JS 优化服务于主线程:代码分割/懒加载/ vendor 分离 → 更快可交互 → 改善 INP;CSR 架构下同样改善 LCP。
- 静态站点 + CDN 是性能最优默认架构:SSG/预渲染让 LCP 资源可在初始 HTML 中被发现、无需额外 JS 即可渲染;配合平台
_headers/purge API 把"HTML 永远新鲜 + 资产永远命中"变成声明式配置。
3. 方法
- 测量:CrUX(免埋点的真实用户数据)打底 →
web-vitals库自建 RUM 补深度 → Lighthouse/DevTools 做 lab 复现与回归拦截。field 与 lab 不一致时以 field 为准。 - 归因:LCP 拆四段并对照理想占比(40/10/40/10);用 TTFB−FCP、FCP−LCP 差值定位"渲染阻塞型"vs"资源发现型"问题。
- 资源工程:图片走 srcset/sizes/picture + 格式/压缩 + 按需变体;JS 走动态 import + 路由级分割 + vendor chunk。
- 缓存与部署:按资源类型设计 Cache-Control 矩阵(HTML: no-cache+ETag;哈希资产: 一年+immutable;个性化: +private),用托管 CDN 的 purge 能力弥补标准 HTTP 无法主动失效的缺陷;部署时以
_headers等平台原语声明。
4. 数据与证据
- 阈值与分位:LCP ≤2.5s / INP ≤200ms / CLS ≤0.1,75 分位,移动与桌面分列(web.dev vitals)。
- LCP 子段理想占比:TTFB ~40%、load delay <10%、load duration ~40%、render delay <10%(web.dev optimize-lcp)。
- ">50% of users abandon a website if it takes longer than 3 seconds to load"(web.dev code splitting)。
- AVIF 相比 JPEG "greater than 50% savings ... in some cases"(web.dev image performance)。
- DPR≥3 时人眼基本无法受益(web.dev image performance);QPACK 把
public, max-age=31536000等列为高频可 1 字节压缩值(MDN)。 - Cloudflare Pages:自定义头不覆盖 Functions 响应、100 条规则/文件、2000 字符/行、redirect 优先(官方文档)。
- INP 于 2024-03 取代 FID;稳定指标一年最多一改(web.dev vitals)。
- 证据充分性评价:阈值与机制类主张均出自第一手官方文档,互相印证(如 MDN 的 cache busting 模式与 Cloudflare 的
_headers示例、web.dev 的"eliminate network time"与 MDN 的 max-age 一年配对);但效益类数据(3 秒放弃率、AVIF 50%)多为 Google/厂商引用的旧研究或"some cases",缺乏本机复现,采信时需打折扣(见局限)。
5. 结论
前端性能优化已经方法论化:它不是技巧集,而是一条可复用的流水线——用 CWV 定靶、用子段拆解归因、用资源工程(图片/JS)与缓存策略两个轮子驱动、最终收敛到"静态化 + CDN + 声明式缓存头"的部署形态。对内容型/文档型站点,"SSG + no-cache 的 HTML + immutable 的哈希资产 + preload/fetchpriority 的 LCP 图"就是接近理论最优的默认配方;单页重交互应用再叠加路由级代码分割。所有优化动作必须回到 field 数据验证,防止"时间转移"式伪优化。
6. 局限(含作者自认与我的推断)
- 指标仍在演化:INP 替代 FID 仅一年余;CWV"一年最多一改"意味着任何建立在具体阈值上的自动化都会随年度更新失效。
- 75 分位是折衷:它隐掉了最差 25% 用户的体验;对转化关键页可能需要更严的内部目标(文档未展开)。
- 时效不齐:code splitting 文更新于 2018 年(围绕 webpack SplitChunksPlugin,未覆盖 Vite/Rollup 原生动态 import 生态或 Turbopack/Rspack);image performance 模块 2023 年更新,未纳入较新的格式降级策略实践。读者应把"机制"当真、"工具链"当历史。
- LCP 四段占比是"指南,非规则"(文档原话),且官方自认 load duration 对多数站点并非瓶颈——压图之外的 render delay/TTFB 常被低估。
- 效益数据的出处强度弱:3 秒放弃率、AVIF 节省均为引用型统计,未给出样本与年代,且"some cases"限定词明显。
- 缓存模型的天花板:标准 HTTP 无法主动删除中间缓存中已存储的长缓存响应(MDN 明示);解法依赖各 CDN 的 purge API,跨平台行为不一(搜索结果亦提示"双 CDN 叠加导致 stale 部署"这类坑,官方文档未覆盖)。
- 图片变体工程无普适答案:多少变体"合理"全靠"it depends"+ 实测,
Accept头方案要求服务端/边缘可编程,纯静态托管只能靠<picture>(HTML 体积膨胀)。 - 推断性局限(非原文自认):Cloudflare 文档仅限自身平台语义(如
CDN-Cache-Control),Netlify/Vercel 默认头策略与之有差异,跨平台迁移时"同一配方"可能产生不同响应头;本笔记未实测各平台,不能断言一致性。
7. 对我的启发(可执行清单)
- 给自己的静态站点定一个"默认部署配方":HTML
no-cache+ ETag/Last-Modified;/assets/*(哈希文件名)public, max-age=31536000, immutable;用_headers文件声明并进版本库,部署即配置。 - 10 行代码上 RUM:
web-vitals+sendBeacon上报三指标,攒自己的 75 分位数据,替代只看 Lighthouse 分数的习惯。 - LCP 图四件套:
fetchpriority="high"、必要时<link rel=preload>、绝不loading="lazy"、与 HTML 同源托管;反过来给轮播图等非首屏图降优先级。 - 图片管线收敛:每图 2–3 个宽度变体(对齐 DPR 2/3 与移动/桌面断点)+ AVIF→WebP→JPEG 的
<picture>,拒绝变体爆炸;hero 图允许比缩略图更多变体。 - JS 默认做路由级分割 + vendor chunk 分离,把"首屏只发当前路由所需"当作构建产物的验收标准。
- 排查顺序纪律:先 CrUX/RUM 确认真问题 → 拆 LCP 四段找最大段 → 逐段优化 → 回 field 验证;严禁"凭感觉压图"。
- 写作/沉淀角度:MDN 的"两种通用缓存模式" + Cloudflare 的
_headers示例可以合成一篇《静态站部署缓存一页纸》,比散读十篇博客更能复用。
四、原始引述索引(重要原文备查)
- "Site owners shouldn't have to be performance experts to understand the quality of experience they are delivering to their users." — Web Vitals
- "a good threshold to measure is the 75th percentile of page loads, segmented across mobile and desktop devices." — Web Vitals
- "Only field measurement can accurately capture the complete picture." — Web Vitals
- "Never lazy-load your LCP image, as that will always lead to unnecessary resource load delay, and will have a negative impact on LCP." — Optimize LCP
- "If prerendering is possible with your architecture, it's generally a better choice for performance." — Optimize LCP
- "Our research shows that the load duration portion of LCP tends not to be a significant bottleneck for most sites." — Optimize LCP
- "basically all responses should explicitly specify a
Cache-Controlheader." — MDN HTTP caching - "The
no-cachedirective does not prevent the storing of responses but instead prevents the reuse of responses without revalidation." — MDN HTTP caching - "There is no way to delete responses on an intermediate server that have been stored with a long
max-age." — MDN HTTP caching - "every additional image variant comes at a cost and makes less efficient use of the browser cache." — Image performance
- "AVIF ... tests have shown greater than 50% savings when compared to JPEG in some cases." — Image performance
- "Over 50% of users abandon a website if it takes longer than 3 seconds to load." — Code splitting
- "Ads are one of the largest contributors to layout shifts on the web." — Optimize CLS
- "
/static/*→Cache-Control: public, max-age=31556952, immutable" — Cloudflare Pages: Customize headers
抓取说明:来源 1–5 经 web_reader 抓取全文(含表格与代码);来源 6–7 经 WebFetch 摘要抓取(引述与数字均来自其返回内容)。INP 取代 FID、CWV 阈值等亦与 Google Search 文档搜索结果交叉印证。本笔记未做任何本地实验,全部证据为文档与检索所得。