页面性能分析
页面加载速度和运行流畅度直接影响用户体验和搜索排名。浏览器开发者工具提供了强大的性能分析面板,帮助你识别性能瓶颈、测量加载时间、分析资源加载情况。本节将详细介绍 Performance 面板、Lighthouse 审计和 Network 瀑布流分析的使用方法。
前置知识
阅读本节前,建议先了解:元素检查与调试
基础概念
页面性能包含两个维度:加载性能(页面从请求到完全呈现所需的时间)和运行时性能(页面交互、动画、滚动等操作的流畅程度)。Performance 面板通过录制运行时活动来分析性能瓶颈,Lighthouse 提供自动化审计评分,Network 面板则聚焦于资源加载的时序分析。
核心 Web 指标
Google 定义的核心 Web 指标(Core Web Vitals)是衡量页面性能的关键标准:
| 指标 | 全称 | 含义 | 良好值 |
|---|---|---|---|
| LCP | Largest Contentful Paint | 最大内容绘制时间 | <= 2.5 秒 |
| FID | First Input Delay | 首次输入延迟 | <= 100 毫秒 |
| CLS | Cumulative Layout Shift | 累计布局偏移 | <= 0.1 |
补充指标
除了核心指标,还有以下常用性能指标:
- FCP(First Contentful Paint):首次内容绘制
- TTI(Time to Interactive):可交互时间
- TBT(Total Blocking Time):总阻塞时间
- Speed Index:速度指数(内容视觉填充速度)
详细说明
Performance 面板
Performance 面板用于录制和分析页面运行时的性能数据,包括 JavaScript 执行、样式计算、布局计算、页面绘制等。
录制步骤:
text
1. 打开 DevTools → Performance 面板
2. 点击 "Record" 按钮(或按 Ctrl+E)开始录制
3. 在页面上进行操作(点击按钮、滚动页面等)
4. 点击 "Stop" 按钮结束录制
5. 分析生成的性能报告性能报告结构:
text
性能报告分为四个主要区域:
1. 概览面板(顶部)
→ FPS:帧率图表(绿色越高越好,低于 60fps 会变红)
→ CPU:CPU 使用率图表(按颜色区分不同活动类型)
→ NET:网络请求活动
2. 火焰图(主体)
→ 以时间线形式展示各活动的耗时
→ 不同颜色代表不同类型的活动:
- 蓝色 = Loading(加载)
- 黄色 = Scripting(脚本执行)
- 紫色 = Rendering(渲染/重排)
- 绿色 = Painting(绘制)
- 灰色 = System(系统事件)
- 空白 = Idle(空闲)
3. 底部详情(底部)
→ 选中某个活动后显示详细信息
4. 摘要统计(底部标签)
→ 显示关键指标数据常见性能瓶颈识别:
text
长任务(Long Tasks):
- 黄色柱状条特别长的区域 = 耗时过长的 JavaScript
- 通常由大量计算、复杂 DOM 操作、同步请求导致
- 修复方案:拆分任务、使用 requestAnimationFrame、避免同步阻塞
重排过多(Layout Thrashing):
- 紫色区域过多或过于频繁
- 通常由频繁读取 offsetWidth/clientHeight 后修改样式导致
- 修复方案:批量读取/写入、使用 transform 代替 top/left
绘制开销大:
- 绿色区域过宽
- 通常由复杂阴影、大面积半透明元素、blur 滤镜导致
- 修复方案:简化视觉效果、使用 will-change 属性提示Network 面板深入分析
Network 面板是分析资源加载性能的核心工具。
瀑布流(Waterfall)解读:
text
瀑布流图表展示了每个资源的加载时序:
|-- DNS Lookup --|-- Connection --|-- SSL --|-- Request --|-- Waiting --|-- Response --|
| | | | | | |
| | | | | | |
各阶段含义:
- DNS Lookup - DNS 域名解析时间
- Connection - 建立 TCP 连接时间
- SSL - SSL/TLS 握手时间(HTTPS)
- Request - 请求发送时间
- Waiting (TTFB) - 服务器响应等待时间(Time to First Byte)
- Content Download - 内容下载时间关键操作:
text
1. 禁用缓存录制:
- 勾选 "Disable cache" 复选框
- 模拟首次访问时的加载性能
2. 查看资源详情:
- 点击某个资源查看 Headers、Preview、Response、Timing
3. 按大小排序:
- 点击 "Size" 列标题,找出最大的资源文件
4. 筛选特定类型:
- 使用过滤按钮只显示 JS、CSS、Img 等
5. 导出 HAR 数据:
- 右键 → "Save all as HAR with content"
- 可用于离线分析或分享给团队成员资源优化建议:
text
优化目标:
- 减少请求数量:合并 CSS/JS 文件,使用 CSS Sprites 或 SVG
- 减小资源体积:压缩图片(WebP 格式)、压缩 CSS/JS(minify)
- 利用缓存:设置合理的 Cache-Control 头
- 使用 CDN:将静态资源部署到 CDN,减少延迟
- 延迟加载:非关键资源使用 loading="lazy"
- 预加载:关键资源使用 <link rel="preload">Lighthouse 审计
Lighthouse 是 Google 开发的开源自动化审计工具,内置于 Chrome DevTools,提供全面的页面质量评估。
运行步骤:
text
1. 打开 DevTools → Lighthouse 面板
2. 选择审计模式:
- Navigation:审计整页加载(推荐)
- Timespan:审计一段时间内的交互性能
- Snapshot:审计当前页面状态
3. 选择设备:
- Mobile:模拟移动设备(推荐先审计此模式)
- Desktop:模拟桌面设备
4. 选择审计类别(可多选):
- Performance - 性能
- Accessibility - 无障碍
- Best Practices - 最佳实践
- SEO - 搜索优化
- PWA - 渐进式应用
5. 点击 "Analyze page load" 开始审计
6. 等待审计完成(通常 30-60 秒)
7. 查看报告报告解读:
text
报告结构:
1. 总评分(顶部)
→ 各维度 0-100 分,圆形仪表盘展示
2. 核心指标(Performance 部分)
→ LCP、FID/INP、CLS 的具体数值
→ 与良好/需改进/较差标准对比
3. 优化建议(主体)
→ 按影响程度排列
→ 每条建议包含:问题描述、优化方案、节省时间
4. 诊断信息(底部)
→ 详细的性能数据汇总常见优化建议及对应方案:
| Lighthouse 建议 | 含义 | 优化方案 |
|---|---|---|
| Remove unused CSS | 存在未使用的 CSS 规则 | 使用 PurgeCSS 等工具移除 |
| Remove unused JavaScript | 存在未使用的 JS 代码 | Tree Shaking、Code Splitting |
| Serve images in next-gen formats | 图片格式不够现代 | 转为 WebP/AVIF 格式 |
| Efficiently encode images | 图片压缩不足 | 使用 ImageOptim、Squoosh 压缩 |
| Use text compression | 资源未启用 Gzip/Brotli | 服务器配置开启压缩 |
| Reduce unused CSS | CSS 文件过大 | 拆分关键 CSS 和非关键 CSS |
| Eliminate render-blocking resources | 阻塞渲染的资源 | 使用 async/defer 加载 JS |
| Properly size images | 图片显示尺寸与实际尺寸不匹配 | 提供适当尺寸的图片 |
实战示例
示例:使用 Network 面板分析页面加载
创建一个包含多种资源类型的页面,用于练习 Network 面板分析:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>性能分析练习</title>
<!-- 模拟阻塞渲染的 CSS(内联在 head 中) -->
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
padding: 2rem;
background: #f8fafc;
color: #1e293b;
}
.container { max-width: 800px; margin: 0 auto; }
h1 { color: #2563eb; margin-bottom: 1rem; }
.card {
background: white;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1rem;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.card h2 { font-size: 1.1rem; margin-bottom: 0.5rem; }
.card p { color: #64748b; line-height: 1.6; }
.metrics {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 1rem;
margin: 1.5rem 0;
}
.metric-card {
background: white;
border-radius: 8px;
padding: 1rem;
text-align: center;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.metric-card .value {
font-size: 1.8rem;
font-weight: bold;
color: #2563eb;
}
.metric-card .label {
font-size: 0.85rem;
color: #64748b;
margin-top: 0.25rem;
}
.instructions {
background: #dbeafe;
border: 1px solid #93c5fd;
border-radius: 8px;
padding: 1rem;
margin-bottom: 1.5rem;
line-height: 1.8;
}
code {
background: #f1f5f9;
padding: 0.15rem 0.4rem;
border-radius: 3px;
font-size: 0.9em;
}
</style>
</head>
<body>
<div class="container">
<h1>性能分析练习场</h1>
<div class="instructions">
<strong>练习步骤:</strong><br>
1. 打开 DevTools → Network 面板<br>
2. 勾选 <code>Disable cache</code><br>
3. 按 Ctrl+Shift+R 强制刷新页面<br>
4. 观察瀑布流中各资源的加载时序<br>
5. 使用 <code>larger-than:1k</code> 过滤大于 1KB 的资源<br>
</div>
<div class="metrics" id="metricsArea">
<!-- 由 JavaScript 动态生成性能数据 -->
</div>
<div class="card">
<h2>关于本页面的资源加载</h2>
<p>本页面是纯 HTML+CSS+JS 页面,没有外部图片和字体依赖。打开 Network 面板可以看到所有请求都来自本地服务器。</p>
</div>
<div class="card">
<h2>性能优化提示</h2>
<p>在实际项目中,常见的性能瓶颈包括:未压缩的图片、未合并的 CSS/JS 文件、阻塞渲染的外部资源、过多的 JavaScript 执行等。</p>
</div>
<div class="card">
<h2>Lighthouse 审计</h2>
<p>切换到 Lighthouse 面板,对当前页面运行审计。尝试对比启用/禁用缓存时的性能差异。</p>
</div>
</div>
<script>
// 模拟使用 performance API 获取页面加载性能数据
window.addEventListener('load', function () {
// 获取 Performance API 的性能数据
var timing = performance.timing;
var navigation = performance.getEntriesByType('navigation')[0];
var metrics = [
{
label: 'DNS 解析',
value: Math.round(navigation.domainLookupEnd - navigation.domainLookupStart) + 'ms'
},
{
label: 'TCP 连接',
value: Math.round(navigation.connectEnd - navigation.connectStart) + 'ms'
},
{
label: 'TTFB',
value: Math.round(navigation.responseStart - navigation.requestStart) + 'ms'
},
{
label: 'DOM 就绪',
value: Math.round(navigation.domContentLoadedEventEnd - navigation.startTime) + 'ms'
},
{
label: '页面完全加载',
value: Math.round(navigation.loadEventEnd - navigation.startTime) + 'ms'
},
{
label: '资源数量',
value: performance.getEntriesByType('resource').length + ' 个'
}
];
// 在 Console 中输出详细性能数据
console.group('页面性能数据');
metrics.forEach(function (m) {
console.log(m.label + ': ' + m.value);
});
console.groupEnd();
// 在页面上显示性能数据
var metricsArea = document.getElementById('metricsArea');
metrics.forEach(function (m) {
var card = document.createElement('div');
card.className = 'metric-card';
card.innerHTML =
'<div class="value">' + m.value + '</div>' +
'<div class="label">' + m.label + '</div>';
metricsArea.appendChild(card);
});
});
</script>
</body>
</html>示例:使用 Performance 面板分析动画
创建一个包含动画的页面,使用 Performance 面板分析帧率:
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>动画性能分析</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: sans-serif;
padding: 2rem;
background: #0f172a;
color: white;
min-height: 100vh;
}
h1 { margin-bottom: 1rem; }
.controls {
margin-bottom: 1.5rem;
display: flex;
gap: 1rem;
}
button {
padding: 0.5rem 1rem;
border: 1px solid #3b82f6;
background: transparent;
color: #3b82f6;
border-radius: 4px;
cursor: pointer;
font-size: 0.95rem;
}
button:hover { background: #1e3a5f; }
button.active { background: #3b82f6; color: white; }
.stage {
width: 100%;
max-width: 800px;
height: 300px;
background: #1e293b;
border-radius: 12px;
position: relative;
overflow: hidden;
}
/* 优化方案:使用 transform 代替 left/top */
.ball-optimized {
width: 50px;
height: 50px;
border-radius: 50%;
background: linear-gradient(135deg, #3b82f6, #8b5cf6);
position: absolute;
top: 50%;
left: 50%;
/* 使用 transform 进行动画,触发 GPU 加速 */
transform: translate(-50%, -50%);
will-change: transform;
}
/* 非优化方案:使用 left/top 定位 */
.ball-unoptimized {
width: 50px;
height: 50px;
border-radius: 50%;
background: linear-gradient(135deg, #ef4444, #f97316);
position: absolute;
top: 50%;
left: 50%;
margin-left: -25px;
margin-top: -25px;
}
.fps-counter {
position: fixed;
top: 1rem;
right: 1rem;
background: rgba(0, 0, 0, 0.7);
padding: 0.5rem 1rem;
border-radius: 4px;
font-family: monospace;
font-size: 0.9rem;
}
.tip {
margin-top: 1rem;
color: #94a3b8;
font-size: 0.9rem;
line-height: 1.6;
}
</style>
</head>
<body>
<h1>动画性能分析</h1>
<div class="controls">
<button id="btnTransform" class="active">transform 动画(优化)</button>
<button id="btnLeftTop">left/top 动画(非优化)</button>
<button id="btnStop">停止</button>
</div>
<div class="stage" id="stage">
<div class="ball-optimized" id="ball"></div>
</div>
<div class="fps-counter" id="fpsCounter">FPS: --</div>
<p class="tip">
提示:按 F12 打开 DevTools → Performance 面板 → 点击录制 →
观察两种动画的 FPS 差异。transform 动画应保持 60fps,
而 left/top 动画可能出现掉帧。
</p>
<script>
var ball = document.getElementById('ball');
var stage = document.getElementById('stage');
var fpsCounter = document.getElementById('fpsCounter');
var animationId = null;
var currentMode = 'transform';
// FPS 计算
var frameCount = 0;
var lastTime = performance.now();
function updateFPS() {
frameCount++;
var now = performance.now();
if (now - lastTime >= 1000) {
var fps = Math.round(frameCount * 1000 / (now - lastTime));
fpsCounter.textContent = 'FPS: ' + fps;
frameCount = 0;
lastTime = now;
}
}
// 优化方案:使用 requestAnimationFrame + transform
function animateTransform(timestamp) {
var t = timestamp / 1000;
var x = Math.sin(t * 2) * (stage.offsetWidth / 2 - 50);
var y = Math.cos(t * 3) * (stage.offsetHeight / 2 - 50);
ball.style.transform = 'translate(calc(-50% + ' + x + 'px), calc(-50% + ' + y + 'px))';
updateFPS();
animationId = requestAnimationFrame(animateTransform);
}
// 非优化方案:使用 requestAnimationFrame + left/top(触发重排)
function animateLeftTop(timestamp) {
var t = timestamp / 1000;
var x = Math.sin(t * 2) * (stage.offsetWidth / 2 - 50);
var y = Math.cos(t * 3) * (stage.offsetHeight / 2 - 50);
ball.style.left = (stage.offsetWidth / 2 + x) + 'px';
ball.style.top = (stage.offsetHeight / 2 + y) + 'px';
updateFPS();
animationId = requestAnimationFrame(animateLeftTop);
}
function startAnimation(mode) {
stopAnimation();
currentMode = mode;
ball.className = mode === 'transform' ? 'ball-optimized' : 'ball-unoptimized';
ball.style.transform = '';
ball.style.left = '50%';
ball.style.top = '50%';
if (mode === 'transform') {
animationId = requestAnimationFrame(animateTransform);
} else {
animationId = requestAnimationFrame(animateLeftTop);
}
}
function stopAnimation() {
if (animationId) {
cancelAnimationFrame(animationId);
animationId = null;
}
}
// 按钮事件
document.getElementById('btnTransform').addEventListener('click', function () {
startAnimation('transform');
this.classList.add('active');
document.getElementById('btnLeftTop').classList.remove('active');
});
document.getElementById('btnLeftTop').addEventListener('click', function () {
startAnimation('leftTop');
this.classList.add('active');
document.getElementById('btnTransform').classList.remove('active');
});
document.getElementById('btnStop').addEventListener('click', stopAnimation);
// 默认启动优化动画
startAnimation('transform');
</script>
</body>
</html>注意事项
- Performance 面板录制会增加开销:录制本身会占用一定的 CPU 资源,可能导致帧率略有下降,这是正常现象
- Network 数据需禁用缓存:分析加载性能时务必勾选 "Disable cache",否则可能看到缓存命中的假象
- Lighthouse 结果受设备影响:不同电脑的硬件配置不同,Lighthouse 分数可能有波动。应关注分数趋势而非单次绝对值
- 首次加载 vs 再次加载:首次访问(无缓存)和再次访问(有缓存)的性能差异巨大,两者都需要测试
- 实验室数据 vs 真实数据:DevTools 测量的是实验室数据,真实用户的网络和设备条件可能更差
最佳实践
- 优先关注 LCP 和 CLS:这两个指标对用户体验影响最大,优先优化
- 图片优化是投入产出比最高的优化:通常图片占页面总体积的 50% 以上,优先压缩图片
- 延迟加载非关键资源:图片使用
loading="lazy",JS 使用defer/async,CSS 进行关键路径优化 - 定期运行 Lighthouse 审计:将 Lighthouse 审计集成到 CI/CD 流程中,确保性能不退化
- 使用 Performance 面板验证动画优化:确保动画保持 60fps,避免使用会触发重排的 CSS 属性
下一节
继续学习:响应式设计模式