Skip to content

页面性能分析

页面加载速度和运行流畅度直接影响用户体验和搜索排名。浏览器开发者工具提供了强大的性能分析面板,帮助你识别性能瓶颈、测量加载时间、分析资源加载情况。本节将详细介绍 Performance 面板、Lighthouse 审计和 Network 瀑布流分析的使用方法。

前置知识

阅读本节前,建议先了解:元素检查与调试

基础概念

页面性能包含两个维度:加载性能(页面从请求到完全呈现所需的时间)和运行时性能(页面交互、动画、滚动等操作的流畅程度)。Performance 面板通过录制运行时活动来分析性能瓶颈,Lighthouse 提供自动化审计评分,Network 面板则聚焦于资源加载的时序分析。

核心 Web 指标

Google 定义的核心 Web 指标(Core Web Vitals)是衡量页面性能的关键标准:

指标全称含义良好值
LCPLargest Contentful Paint最大内容绘制时间<= 2.5 秒
FIDFirst Input Delay首次输入延迟<= 100 毫秒
CLSCumulative 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 CSSCSS 文件过大拆分关键 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 属性

下一节

继续学习:响应式设计模式

参考链接