前端性能 · 从零到生产

🚀 前端性能分析完全指南

Chrome DevTools 怎么用?Core Web Vitals 是什么?Lighthouse 怎么看?生产环境怎么监控?
你完全空白没关系,我从打开F12开始教。

📖 10个专题 🖼️ 40+张图解 💻 完整DevTools截图模拟 🏭 生产真实场景
Chrome DevTools Performance面板 Core Web Vitals Lighthouse 渲染性能 生产监控
📊

P1. 性能指标全览 — 先搞清楚"快"是什么意思

在优化之前,你必须知道要优化什么、怎么衡量

你每天打开微信、刷抖音、点美团外卖——有没有想过,为什么有些App打开"嗖"的一下就好了,有些要等3秒白屏?这些"快"和"慢"怎么量化?一秒钟?两秒钟?用什么工具量?这是性能优化的起点。
📖 用户感受 vs 技术指标

想象你打开手机上的一个App:

⏱️ 0-100ms:感觉"瞬间响应" — 就像你刚点开微信,聊天列表就出来了

⏱️ 100-300ms:感觉"很快" — 就像你点开美团,不到半秒看到商家

⏱️ 300-1000ms:感觉"还行" — 就像你打开一个网页,等了不到1秒

⏱️ 1-3秒:感觉"有点慢" — 你会下意识想"是不是卡了"

⏱️ 3秒+:感觉"挂了" — 你会关掉重来或者直接卸载

⏱️ 10秒+:用户直接关页面 — 据Google统计,53%的移动用户会离开加载超过3秒的页面

💡 关键认知:性能不是"感觉快",是有明确数字指标的
行业通用标准是 Google 提出的 Core Web Vitals(核心网页指标),包含 3 个具体指标:
1. LCP(Largest Contentful Paint)— 最大内容渲染时间
2. INP(Interaction to Next Paint)— 交互到下一次绘制的延迟
3. CLS(Cumulative Layout Shift)— 累积布局偏移
📊 前端性能关键指标一览
前端性能指标全景图
指标名
含义
FCP
First Contentful Paint — 浏览器第一次画出内容(文字/图片)的时间
≤1.8s
>3.0s
LCP ⭐
Largest Contentful Paint — 页面最大一块内容(大图/标题)渲染出来的时间
≤2.5s
>4.0s
INP ⭐
Interaction to Next Paint — 用户点击后到页面做出视觉响应的延迟
≤200ms
>500ms
CLS ⭐
Cumulative Layout Shift — 页面加载过程中元素"乱跳"的程度
≤0.1
>0.25
TTFB
Time to First Byte — 浏览器发出请求到收到服务器第一个字节的时间
≤800ms
>1800ms
TTI
Time to Interactive — 页面完全可交互(JS加载完+事件绑定好)的时间
≤3.8s
>7.3s
TBT
Total Blocking Time — FCP到TTI之间,长任务阻塞主线程的总时间
≤200ms
>600ms
⭐ 标记的是 Core Web Vitals — Google 排名算法会用到的指标,直接影响SEO!
🔍 你在生产中的工作流:
1. 先用 Lighthouse 跑一遍,看总评分和各指标数值
2. 用 Chrome DevTools Performance 面板录一段操作,看具体哪里慢
3. 用 Network 面板看每个请求的瀑布流,找阻塞渲染的资源
4. 针对性优化后,再用 Lighthouse 验证是否改善
5. 上线后用 Sentry/自建监控 持续观察真实用户数据
🛠️

P2. Chrome DevTools 面板总览 — 你最重要的武器

F12打开的就是它,先认识每个面板干嘛的

你刚入职,组长说"这个页面加载慢,你用DevTools看看"。你按F12打开了一堆面板,完全不知道从哪看起。没关系,我来一个个讲。
🔧 DevTools 面板功能速查
Chrome DevTools — 面板总览
面板名
干什么的
性能相关度
Elements
查看/编辑DOM结构和CSS样式。看元素的盒模型、computed样式
⭐⭐
Console
看JS报错、打印日志。性能相关的 error/warn 在这里
⭐⭐
Sources
查看源码、打断点调试。能找到性能瓶颈的具体代码行
⭐⭐
Network
所有网络请求的瀑布流!看每个资源加载时间、大小、HTTP状态码
⭐⭐⭐⭐⭐
Performance
录制页面操作,看CPU使用、帧率、渲染、JS执行的时间线
⭐⭐⭐⭐⭐
Memory
分析内存泄漏!堆快照(Heap Snapshot)、分配时间线(Allocation Timeline)
⭐⭐⭐⭐
Application
看Storage(LocalStorage/SessionStorage/Cookie)、Service Worker、缓存
⭐⭐⭐
Lighthouse
一键跑审计!打分+建议。新手最友好的性能分析入口
⭐⭐⭐⭐⭐
📖 性能排查三板斧(生产中最常用)

不要一上来就全部面板都看。实际工作中,90%的性能问题只需要这三个面板:

1️⃣ Network 面板 — 页面加载慢?先看这里。看哪个资源太大、哪个请求太慢

2️⃣ Performance 面板 — 页面卡顿?录制一段操作,看主线程在干什么

3️⃣ Lighthouse — 不知道从哪开始?先跑一次审计,它会告诉你该优化什么

💡 打开 DevTools 的方式

🖱️ 方式1:右键页面 → "检查"(Inspect)

⌨️ 方式2F12(Windows)/ Cmd+Option+I(Mac)

⌨️ 方式3Ctrl+Shift+I(Windows)/ Cmd+Option+I(Mac)

🔍 方式4Ctrl+Shift+C 直接进入元素选择模式(可以直接点页面上的元素)

生产小技巧:在DevTools设置(齿轮图标)中勾选 "Disable cache (while DevTools is open)",这样每次刷新都不会用缓存,模拟用户第一次访问的效果。
🌊

P3. Network 瀑布流 — 看每个请求在干什么

页面加载慢,99%的情况先来这里找原因

你打开美团外卖首页,等了好几秒才看到完整内容,运营反馈"太慢了用户都跑了"。你打开Network面板,按F5刷新,看到了密密麻麻的瀑布流。每个彩色的条条是什么意思?哪个在拖慢整体?怎么分析?
🌊 瀑布流是什么?
📖 瀑布流原理:你打开美团外卖的加载过程

想象你打开美团外卖首页,页面的各个部分是按顺序加载的:

📄 HTML骨架(搜索栏框架)— 先拿到HTML骨架,浏览器才知道页面结构

🎨 CSS样式(搜索栏变好看了)— 拿到CSS才知道怎么渲染,否则只有裸奔的文字

🔧 JS功能(交互就绪)— JS下载+执行完才能点击、滚动、搜索

🖼️ 图片资源(商家logo、菜品图片)— 不影响操作,但没加载完页面不完整

📡 API数据(商家列表)— 后端返回的动态数据,通常等JS执行完才调接口

瀑布流就是时间线——每个资源什么时候开始请求、下载了多久、什么时候结束。横轴是时间,每个资源一行。

📊 瀑布流实战:美团外卖首页加载分析
Chrome DevTools — Network 瀑布流(美团外卖首页)
#
Name
Size
Time
Waterfall
1
index.html
12 KB
180ms
TTFB 45ms → 下载 135ms
2
main.css
85 KB
320ms
等待HTML → 下载 320ms
3
vendor.js 🔴
1.8MB
2.4s
等待HTML → 下载 2.4s ⚠️ 太大了!
4
app.js
320 KB
450ms
下载 450ms
5
api/products
45 KB
680ms
等JS执行完 → 接口调用 680ms
6
banner.jpg
2.1MB
1.8s
下载 1.8s
7
product1.png
350 KB
280ms
下载 280ms
8
product2.png
280 KB
220ms
下载 220ms
🔴 关键发现:
① vendor.js 1.8MB 下载 2.4s — 这是最慢的资源,阻塞页面渲染!
② banner.jpg 2.1MB — 未压缩的巨大图片,轮播图加载是瓶颈
③ api/products 等JS执行完才调 — JS阻塞了数据获取,总共3.5秒才看到完整页面
🔍 Network 面板的关键信息

每个请求的耗时拆解(点击请求 → Timing标签):

Queueing
等待浏览器分配连接(浏览器对同一域名最多6个并发连接)
Stalled
等待建立连接(可能被浏览器排队)
DNS Lookup
域名解析(第一次访问才有,之后有DNS缓存)
Initial conn.
TCP握手(HTTPS还有TLS协商,花的时间更多)
Waiting(TTFB)
发送请求→收到服务器第一个字节(TTFB,反映后端处理速度)
Download
下载内容(资源大小÷网速)
生产经验:怎么看 TTFB 是否正常?
• TTFB < 200ms:正常(静态资源或快速API)
• TTFB 200-500ms:可以接受
• TTFB > 500ms:后端可能有问题(慢查询、服务端渲染慢)
• TTFB > 1s:严重!后端一定有瓶颈,去看后端日志和数据库慢查询
🛠️ Network 面板实用技巧

1. 过滤请求类型:点顶部的 Fetch/XHR 只看API请求,JS 只看JS文件,CSS 只看样式,Img 只看图片

2. 禁用缓存:勾选 Disable cache,模拟首次访问

3. 模拟慢速网络:下拉菜单选 Slow 3G / Fast 3G,模拟移动端弱网环境

4. 查看请求大小:底部状态栏显示总请求数、总传输大小、总加载时间

5. 关注红色请求:失败(4xx/5xx)的请求会显示红色,优先处理

6. 右键请求 → Copy as cURL:可以直接在终端复现这个请求,排查后端问题

⏱️

P4. Performance 面板 — 录制页面操作,看CPU在干什么

页面卡顿、动画掉帧、操作延迟——全靠它排查

你刷网页列表的时候明显感觉卡(比如刷微博、刷知乎),但Network面板看请求都正常。这时候就要用Performance面板录制一段滚动操作,看看到底是什么在消耗CPU。
🎬 怎么用 Performance 面板?
Chrome DevTools — Performance 面板使用步骤
1
打开 Performance 面板
F12 → 点顶部的 Performance 标签
2
点击录制按钮(⏺ 圆形按钮)
或者按 Ctrl+E(Mac: Cmd+E)开始录制
3
在页面上执行你想分析的操作
比如:滚动页面、点击按钮、输入文字、切换Tab
4
再次点击录制按钮停止录制
DevTools 会生成一张完整的时间线图表
!
⚠️ 重要:录制前先做什么
勾选 Screenshots(截图)和 Web Vitals(核心指标)选项。录制时间控制在 5-10 秒,太长数据太多看不清。
📊 Performance 面板长什么样?
Performance 面板 — 录制结果解读
FPS
58 fps
CPU
65%
NET
3 req
Main
Parse HTML
Evaluate Script
🔥 Long Task: 280ms
Layout
Paint
Composite
Event: scroll
紫色 = Scripting(JS执行)
绿色 = Rendering(布局+绘制)
橙色 = Painting(像素绘制)
青色 = System(系统开销)
🔴 关键发现:主线程有一个 Long Task 280ms!这就是卡顿的原因。
浏览器要在 16ms 内完成一帧(60fps),超过 50ms 的任务就是"长任务"。
点击这个紫色块,底部 Bottom-Up 标签能看到具体是哪个函数耗时最长。
🔍 Performance 面板的核心概念
📖 关键概念解释

1. FPS(每秒帧数):60fps是流畅的底线。低于30fps肉眼就能感觉卡。FPS条绿色=好,红色=差。

2. Main Thread(主线程):JS执行、DOM操作、CSS计算、布局、绘制全在主线程。如果主线程被占满了,用户操作就没响应。

3. Long Task(长任务):任何超过50ms的任务都是长任务。每个长任务都会导致输入延迟(INP变差)。

4. 帧时间线:每一帧(16.6ms)要做的事情:JS → Style → Layout → Paint → Composite。任何一步超时都会掉帧。

✅ 生产最佳实践:
• 录制时只做你想要分析的那个操作(不要顺便浏览其他页面)
• 录制 5-10 秒就够了,太长数据杂乱
• 重点关注 Main 区域的红色三角形(⚠️)标记 — 那是浏览器标记的性能问题
• 点击任意一个任务块,底部面板看 Bottom-Up(哪个函数最耗时)和 Call Tree(调用链)
• 用 Event Log 标签看所有事件的时序
🎯

P5. Core Web Vitals — Google最看重的3个指标

LCP、INP、CLS — 直接影响SEO排名,生产必须关注

Google 在2021年把 Core Web Vitals 纳入了搜索排名算法。也就是说,你的页面如果这三个指标差,Google会给你降权,搜索排名下降,流量直接减少。这不是"锦上添花",是"不做就亏"。
🎯 指标1: LCP — 最大内容渲染时间
📖 LCP是什么?用户打开页面看到"主要内容的瞬间"

想象你打开微信的那一刻:

🕐 FCP(First Contentful Paint) — 你看到顶部"微信"两个字出来的瞬间(可能还只看到骨架屏或者标题)

🕐 LCP(Largest Contentful Paint) — 聊天列表完整显示出来了!白屏→看到内容,你等了多久?1秒觉得正常,3秒觉得慢,5秒以为挂了。最大一块内容渲染出来的瞬间

最大内容通常是:一张大Banner图、一段大标题文字、一个大的内容区块。

1.8s
好 ≤2.5s
3.5s
需改善 2.5-4s
6.2s
差 >4.0s

LCP 优化方案:

✅ 服务端优化
• CDN加速(用户就近访问)
• 减少TTFB(后端接口快)
• SSR/SSG(服务端渲染)
✅ 前端优化
• 图片压缩 + WebP格式
• preload关键资源
• 减少JS阻塞(async/defer)
// ✅ preload 关键图片 — 让浏览器提前加载LCP图片
<link rel="preload" as="image" href="/banner.webp" fetchpriority="high">

// ✅ img 标签优化
<img src="/banner.webp"
     loading="eager"          // LCP图片不要懒加载!
     fetchpriority="high"     // 高优先级
     width="1200" height="400" // 提前声明宽高,避免CLS
     decoding="async"          // 异步解码
     alt="页面Banner">
🎯 指标2: INP — 交互到下一次绘制的延迟
📖 INP是什么?用户点击后多久能看到反馈

你在微信里点"发送",手指按下去到看到消息气泡出来:

好的体验(<200ms):点了立刻看到消息气泡弹出来,感觉"跟手",灵敏

🐢 差的体验(>500ms):点了半天没反应,你以为没发出去又点了一次

INP = 从用户操作(点击/输入/按键)到浏览器完成下一次画面更新的时间

80ms
好 ≤200ms
350ms
需改善 200-500ms
800ms
差 >500ms

INP 优化方案:

// ✅ 1. 拆分长任务 — yield to main thread
// ❌ 一次性处理1000条数据 → 阻塞主线程200ms
function processData(items) {
  items.forEach(item => heavyComputation(item)); // 阻塞!
}

// ✅ 拆成小块,每块之间让出主线程给浏览器渲染
async function processDataChunked(items, chunkSize = 50) {
  for (let i = 0; i < items.length; i += chunkSize) {
    const chunk = items.slice(i, i + chunkSize);
    chunk.forEach(item => heavyComputation(item));
    await new Promise(r => setTimeout(r, 0)); // 让出主线程!
  }
}

// ✅ 2. 使用 scheduler.yield()(现代浏览器API)
async function handleClick() {
  // 先做紧急的UI更新
  showLoading();
  await scheduler.yield(); // 让浏览器先渲染loading
  // 再做重计算
  processData(largeDataset);
}

// ✅ 3. Web Worker — 把重计算移到后台线程
const worker = new Worker('heavy-task.js');
worker.postMessage({ data: largeDataset });
worker.onmessage = (e) => {
  // 收到结果后更新UI — 主线程全程不阻塞
  updateUI(e.data);
};
🎯 指标3: CLS — 累积布局偏移
📖 CLS是什么?页面加载时元素"乱跳"的程度

你打开手机网页,看到「限时特价 ¥12」正要点击——突然上面的广告位加载完,页面往下推了一格,你点到了「立即购买 ¥99」!

这就是布局偏移——页面元素在你正要操作的时候突然"跳了"。常见原因:

1. 图片/广告位没有预留空间,加载后把下面的内容挤下去

2. 字体加载慢,先用系统字体渲染,自定义字体加载完文字宽度变了导致跳动

3. 异步插入的DOM元素(如弹窗、广告)把原有内容推开

0.02
好 ≤0.1
0.18
需改善 0.1-0.25
0.45
差 >0.25

CLS 优化方案:

// ✅ 1. 图片/视频必须设置宽高 — 提前预留空间
<!-- ❌ 没有宽高,图片加载后布局跳动 -->
<img src="/product.jpg" alt="商品图片">

<!-- ✅ 设置宽高,浏览器提前预留空间 -->
<img src="/product.jpg" width="300" height="200"
     style="aspect-ratio: 3/2;"  // 用 aspect-ratio 保持比例
     alt="商品图片">

// ✅ 2. 字体优化 — font-display
@font-face {
  font-family: 'CustomFont';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap;  // 先用系统字体,加载完再替换(不会跳动)
}

// ✅ 3. 动态内容预留空间
<!-- 广告位预留300x250空间,即使广告没加载也不会跳动 -->
<div style="min-height: 250px;">
  <!-- 广告内容异步加载到这里 -->
</div>

// ✅ 4. 使用 CSS contain 属性(现代浏览器)
.card-container {
  contain: layout;  // 告诉浏览器这个容器内部变化不影响外部布局
}
💡

P6. Lighthouse 审计 — 一键跑分,告诉你该优化什么

新手的最佳入口,一键生成性能报告+具体建议

你负责一个H5页面的性能优化,但不知道从哪里开始。组长说"先跑个Lighthouse看看"。打开DevTools里的Lighthouse面板,点了一下Analyze,等了30秒——生成了一张打分报告。
💡 Lighthouse 怎么跑?
Chrome DevTools — Lighthouse 面板
1
F12 → 点 Lighthouse 标签(最后面一个💡图标)
2
勾选 Performance(必选)+ 其他你想看的类别(Accessibility/SEO/Best Practices)
3
选择 Mobile(模拟手机)或 Desktop(桌面端)
!
点击 Analyze page load,等 20-40 秒出结果
📊 Lighthouse 报告长什么样?
Lighthouse 报告 — H5页面性能审计
38
Performance
72
Accessibility
91
Best Practices
68
SEO
📊 Core Web Vitals 实测值
4.8s
LCP 🔴
目标 ≤2.5s
280ms
INP 🟡
目标 ≤200ms
0.05
CLS 🟢
目标 ≤0.1
🔧 优化建议(按节省时间排序)
🔴 Properly size images(图片未压缩)
节省 2.1s
🔴 Eliminate render-blocking resources(CSS/JS阻塞渲染)
节省 1.4s
🟡 Remove unused JavaScript(无用JS没删)
节省 0.8s
🟡 Serve images in next-gen formats(用WebP/AVIF)
节省 0.6s
🟢 Use HTTP/2(用HTTP/2多路复用)
节省 0.3s
✅ Lighthouse 使用建议:
• 评分 90+ 是绿色(好),50-89 是橙色(需改善),<50 是红色(差)
重点看 "Opportunities" 部分——它告诉你具体做什么能省多少时间
• 跑分时用 无痕模式(Incognito),避免浏览器插件干扰结果
• 跑 3 次取平均值,单次可能波动大
• 可以用命令行 npx lighthouse https://xxx.com --output html --output-path report.html 自动化跑分
🎨

P7. 渲染流水线 — 浏览器怎么把代码变成画面

理解重排(Reflow)/重绘(Repaint)/合成(Composite),知道怎么写不卡的代码

你改了一个元素的背景色,页面卡了一下;你改了一个元素的宽高,页面卡了好几帧。为什么?因为两种操作触发的渲染步骤不一样。
🎨 浏览器渲染的5个步骤
浏览器渲染流水线 — 从代码到像素的5个步骤
📝
1. JS/CSS
修改DOM/CSSOM
修改 DOM/CSS
📐
2. Style
计算样式
匹配CSS规则
📏
3. Layout
计算位置/大小
重排(Reflow)
🖌️
4. Paint
绘制像素
重绘(Repaint)
🧩
5. Composite
合成图层
GPU合成输出
⚡ 不同操作触发不同步骤:
你改了什么触发的步骤代价例子
改位置/大小Layout → Paint → Composite🔴 最贵width/height/top/left/margin
改颜色/背景Paint → Composite🟡 中等color/background/border-color
改 transform/opacityComposite only🟢 最便宜transform: translateX(10px)
🛠️ 实战:用 transform 替代 top/left 做动画
❌ 触发 Layout(卡顿)
/* 每帧都要重新计算布局 */ .box { position: absolute; top: 0; transition: top 0.3s; } .box:hover { top: 100px; /* Layout+Paint+Composite */ }
✅ 只触发 Composite(流畅)
/* 只触发合成,GPU加速 */ .box { transform: translateY(0); transition: transform 0.3s; will-change: transform; /* 提示浏览器创建独立图层 */ } .box:hover { transform: translateY(100px); /* Composite only! */ }
⚠️ 生产注意:will-change 不要滥用!每个 will-change 都会创建一个独立的合成层,太多会消耗大量GPU内存。只在频繁动画的元素上用,动画结束后移除。
📖 重排 vs 重绘 — 面试常考

重排(Reflow/Layout):你重新布置整个房间——改了元素的宽高位置,所有元素都要重新计算位置。最昂贵的操作。

重绘(Repaint):你只是换了墙纸颜色——改了颜色、背景,位置没变,只需要重新画。中等代价。

合成(Composite):你只是把一张照片移动了一下——只改transform/opacity,不需要重新计算布局。最便宜,GPU直接搞定。

生产经验:transformopacity 做动画!这两个属性不触发布局和绘制,GPU直接处理,60fps轻松达到。

🖼️

P8. 图片与资源优化 — 生产中最容易出问题的地方

图片通常占页面总大小的50-70%,优化图片=优化一半性能

一个H5页面首页加载4.8秒,Lighthouse报告里"Properly size images"节省2.1秒排第一。你打开Network面板一看:banner.jpg 2.1MB、product1.png 350KB、product2.png 280KB。这些图片占了总传输量的80%!
🖼️ 图片优化三板斧
图片格式对比 — 选对格式省70%体积
格式适用场景压缩率透明度浏览器支持
JPEG照片、渐变色图片✅ 全部
PNG图标、截图、需要透明✅ 全部
WebP ⭐照片+图标(通用)高(比JPEG小30%)✅ 97%+
AVIF最新格式(体积最小)极高(比WebP小20%)⚠️ 92%+
SVGLogo、图标(矢量图)极小✅ 全部
✅ 生产推荐:照片用 WebP/AVIF,图标用 SVG,格式兼容用 <picture> 标签
// ✅ 1. 使用 <picture> 标签 — 格式兼容 + 响应式图片
<picture>
  <!-- 浏览器按顺序选第一个能识别的格式 -->
  <source srcset="/banner.avif" type="image/avif">
  <source srcset="/banner.webp" type="image/webp">
  <img src="/banner.jpg" alt="页面Banner"
       width="1200" height="400"
       loading="lazy"              // 非首屏图片懒加载
       decoding="async">          // 异步解码
</picture>

// ✅ 2. 响应式图片 — 不同屏幕加载不同大小
<img srcset="/product-300.webp 300w,
             /product-600.webp 600w,
             /product-1200.webp 1200w"
     sizes="(max-width: 600px) 100vw, 50vw"
     src="/product-600.webp" alt="商品图片">
// 浏览器自动选择最适合当前屏幕大小的图片

// ✅ 3. 图片压缩命令行工具
// 安装: npm install -g sharp-cli
// 批量转WebP:
sharp -i "./images/*.jpg" -f webp -o "./images/webp/" --quality 80

// ✅ 4. Vite构建时自动压缩图片
// npm install vite-plugin-imagemin
import viteImagemin from 'vite-plugin-imagemin'
export default {
  plugins: [
    viteImagemin({
      gifsicle: { optimizationLevel: 7 },
      optipng: { optimizationLevel: 7 },
      mozjpeg: { quality: 80 },
      pngquant: { quality: [0.8, 0.9] },
      svgo: { plugins: [{ name: 'removeViewBox' }] },
      webp: { quality: 80 }  // 自动生成WebP版本
    })
  ]
}
📦 JS/CSS 资源优化

1. 消除渲染阻塞资源:

// ❌ CSS阻塞渲染 — 浏览器要等CSS下载完才能画任何东西
<link rel="stylesheet" href="/main.css">

// ✅ 非关键CSS异步加载
<link rel="stylesheet" href="/critical.css">    <!-- 首屏关键CSS同步 -->
<link rel="preload" href="/non-critical.css" as="style"
      onload="this.onload=null;this.rel='stylesheet'">  <!-- 非关键CSS异步 -->

// ❌ JS阻塞HTML解析
<script src="/vendor.js"></script>

// ✅ 加 defer/async
<script src="/vendor.js" defer></script>    <!-- HTML解析完再执行 -->
<script src="/analytics.js" async></script>  <!-- 下载完立即执行(不依赖DOM) -->

2. prefetch / preload 预加载:

// preload — 当前页面马上要用到的资源,提前下载
<link rel="preload" href="/fonts/custom.woff2" as="font" crossorigin>
<link rel="preload" href="/banner.webp" as="image">

// prefetch — 下一页面可能用到的资源,空闲时下载
<link rel="prefetch" href="/admin-page.js" as="script">

// dns-prefetch — 提前解析域名(减少DNS查找时间)
<link rel="dns-prefetch" href="//api.example.com">
<link rel="dns-prefetch" href="//cdn.example.com">

// preconnect — 提前建立连接(DNS+TCP+TLS)
<link rel="preconnect" href="https://api.example.com">
💾

P9. HTTP 缓存策略 — 用户第二次访问为什么秒开

强缓存 vs 协商缓存 vs Service Worker,面试生产都常考

一个网页第一次打开要4秒,但关掉再打开只要0.5秒。为什么?因为浏览器缓存了上次的资源。但怎么让用户既能享受缓存的速度,又能在资源更新时拿到最新的?这需要你理解缓存策略。
💾 缓存的三个层级
HTTP 缓存决策流程
1. 浏览器发起请求 → 先查本地缓存
↓ 有缓存?
2a. 强缓存命中 ✅
Cache-Control: max-age=31536000
不发请求!直接用本地缓存
⚡ 0ms — 瞬间
2b. 强缓存过期
max-age 过期了
发请求问服务器:我的缓存还新鲜吗?
⏱ 发一个轻量请求
↓ 服务器怎么回答?
3a. 协商缓存命中 ✅
304 Not Modified
服务器说"没变,继续用缓存"
⚡ 只传输Headers,不传Body
3b. 缓存失效 ❌
200 OK + 新资源
服务器说"变了,给你新的"
⏱ 完整下载新资源
🛠️ 生产中的缓存配置
// ✅ Nginx 缓存配置(生产最常用)

// 1. 带hash的静态资源(JS/CSS/字体)— 永久缓存
// 文件名如 main.a3f8b2c.js — 改了文件hash就变了,缓存自然失效
location /assets/ {
  expires 1y;                          // 1年强缓存
  add_header Cache-Control "public, immutable";  // immutable=永远不验证
  // 用户只要不删缓存,永远不发请求!
}

// 2. HTML文件 — 不缓存或短缓存
location / {
  add_header Cache-Control "no-cache";  // 每次都协商缓存
  // 因为HTML里引用了带hash的JS/CSS路径,HTML变了就会加载新的JS/CSS
}

// 3. API接口 — 不缓存
location /api/ {
  add_header Cache-Control "no-store";  // 完全不缓存
  // 每次都要拿最新数据
}

// ✅ 协商缓存的两种方式(Nginx自动处理)

// 方式1: ETag(文件内容hash)
// 响应头: ETag: "abc123"
// 下次请求头: If-None-Match: "abc123"
// 服务器比对ETag,一样就返回304

// 方式2: Last-Modified(最后修改时间)
// 响应头: Last-Modified: Thu, 12 Jun 2026 08:00:00 GMT
// 下次请求头: If-Modified-Since: Thu, 12 Jun 2026 08:00:00 GMT
// 服务器比对时间,没改就返回304
🔍 面试常考:强缓存 vs 协商缓存
强缓存:不发请求,直接用本地缓存(Cache-Control: max-age=xxx)。就像你手机里的微信聊天记录——上次打开过,这次打开直接显示,完全不用联网。但你朋友发了新消息你看不到,因为是"缓存"
协商缓存:发一个请求问服务器,304就继续用缓存(ETag / Last-Modified)。就像你打开淘宝,先看看有没有新活动(发个请求问服务器"banner变了吗?"),没变就用本地缓存的,变了就下载新的
no-cache:名字叫"不缓存"但实际是"每次都协商"(不是完全不缓存!)
no-store:真正不缓存,每次都重新下载
immutable:告诉浏览器这个资源永远不会变,连协商都不用(配合带hash的文件名)
📡

P10. 生产性能监控 — 线上用户到底体验如何

Lighthouse只在本地跑,真实用户的网络/设备千差万别

你在办公室用MacBook+光纤测得页面1秒加载,感觉很完美。但线上有用户用着三年前的安卓手机+4G网络,实际加载可能要6秒。你根本不知道这些用户的体验,直到客服接到投诉。生产监控就是在真实用户身上采集性能数据。
📡 方案1: 自建Web Vitals上报
// ✅ 使用 web-vitals 库(Google官方出品)
// npm install web-vitals
import { onLCP, onINP, onCLS, onFCP, onTTFB } from 'web-vitals';

// 采集指标并上报
function reportMetric(metric) {
  const { name, value, rating } = metric;
  // name: 'LCP' | 'INP' | 'CLS' | 'FCP' | 'TTFB'
  // value: 具体数值(ms或分数)
  // rating: 'good' | 'needs-improvement' | 'poor'

  // 用 navigator.sendBeacon 上报(页面关闭也能发出去)
  const body = JSON.stringify({
    name, value, rating,
    url: location.href,
    userAgent: navigator.userAgent,
    timestamp: Date.now(),
    // 可以加自定义维度
    userId: getCurrentUserId(),
    pageType: 'product-list',
  });

  navigator.sendBeacon('/api/perf-report', body);

  // 或者用 Image 打点(更兼容)
  // new Image().src = `/api/perf.gif?name=${name}&value=${value}&rating=${rating}`;
}

// 注册监听
onLCP(reportMetric);   // 最大内容渲染
onINP(reportMetric);   // 交互延迟
onCLS(reportMetric);   // 布局偏移
onFCP(reportMetric);   // 首次内容绘制
onTTFB(reportMetric);  // 首字节时间
📡 方案2: Sentry 性能监控
// ✅ Sentry 初始化(已有错误监控的项目加几行就行)
import * as Sentry from '@sentry/vue';

Sentry.init({
  dsn: 'https://xxx@sentry.io/123',
  integrations: [
    Sentry.browserTracingIntegration(),   // 性能追踪
    Sentry.replayIntegration(),           // 用户行为回放
  ],
  tracesSampleRate: 0.1,   // 采样10%(全采太贵)
  // 自动采集: 页面加载、接口请求、用户操作的性能数据

  // 设置用户信息
  initialScope: {
    user: { id: getCurrentUserId() },
    tags: { env: 'production' },
  },
});

// Sentry 自动采集的指标:
// - Web Vitals (LCP, FID, CLS, TTFB)
// - 接口请求耗时
// - 页面路由切换耗时
// - 长任务 (Long Task)
// - 资源加载失败
📡 方案3: 自定义性能上报(后端接口)
// ✅ Spring Boot 后端接收性能数据
@RestController
@RequestMapping("/api/perf")
public class PerfReportController {

    @Autowired
    private PerfMetricRepository repo;

    @PostMapping("/report")
    public ResponseEntity<Void> report(@RequestBody PerfMetric metric) {
        // 存储:指标名、值、评级、URL、UA、时间戳、用户ID
        repo.save(metric);
        return ResponseEntity.ok().build();
    }
}

// ✅ 管理后台查看性能数据
// SQL示例: 查看各页面的LCP P75(第75百分位)— 这才是真实用户体验
// SELECT
//   url,
//   PERCENTILE_CONT(0.75) WITHIN GROUP (ORDER BY value) as p75_lcp,
//   COUNT(*) as sample_count
// FROM perf_metrics
// WHERE name = 'LCP'
//   AND timestamp >= NOW() - INTERVAL '7 days'
// GROUP BY url
// ORDER BY p75_lcp DESC;
📊 生产性能大盘看什么?
生产性能大盘 — 关键指标
3.8s
LCP P75
目标 ≤2.5s
320ms
INP P75
目标 ≤200ms
0.06
CLS P75
目标 ≤0.1
LCP趋势(最近7天)
周一周二周三周四周五周六周日
⚠️ 注意:性能数据要看 P75(第75百分位) 而不是平均值!
平均值会被极端值拉偏。P75表示"75%的用户体验在这个数值以内",更能反映大多数用户的真实感受。
📋 前端性能优化 Checklist
🏗️ 构建优化
✅ Code Splitting(路由级懒加载)
✅ Tree Shaking(按需引入)
✅ 资源加hash(长期缓存)
✅ 压缩JS/CSS(Terser/cssnano)
✅ 图片自动转WebP
🌐 加载优化
✅ CDN加速静态资源
✅ preload关键资源
✅ 非关键CSS/JS异步加载
✅ 图片懒加载(首屏除外)
✅ HTTP/2 多路复用
🎨 渲染优化
✅ 用transform/opacity做动画
✅ 避免强制同步布局
✅ 虚拟滚动处理大列表
✅ 图片设置width/height
✅ font-display: swap
📡 监控告警
✅ Web Vitals采集上报
✅ Sentry错误+性能监控
✅ LCP > 4s 钉钉告警
✅ 每周性能趋势报告
✅ 发版前后对比性能