Chrome DevTools 性能分析完全指南

HTMLPAGE 团队
18 分钟阅读

深入掌握 Chrome DevTools 性能面板的使用技巧,从录制分析到火焰图解读,学会诊断和优化网页性能瓶颈的完整方法论。

#Chrome DevTools #性能分析 #火焰图 #性能优化 #前端调试

Chrome DevTools 性能分析完全指南

为什么需要性能分析工具

在前端开发中,我们经常会遇到这样的问题:"页面加载慢"、"滚动卡顿"、"动画不流畅"。但仅凭直觉很难定位问题的根源。

Chrome DevTools 的 Performance 面板就是为解决这个问题而生的。它能够:

  1. 精确测量:记录页面的每一帧、每一次函数调用
  2. 可视化呈现:通过火焰图直观展示性能瓶颈
  3. 深度分析:揭示 JavaScript 执行、布局计算、渲染绑定的耗时
  4. 量化改进:提供可对比的性能指标
性能问题诊断流程:

用户反馈 → 复现问题 → 录制性能 → 分析火焰图 → 定位瓶颈 → 优化代码 → 验证改进
    ↑                                                                    ↓
    └──────────────────── 持续监控 ←─────────────────────────────────────┘

Performance 面板核心功能

打开和配置面板

打开 Chrome DevTools(F12 或 Cmd+Option+I),切换到 Performance 标签页。

Performance 面板布局:

┌─────────────────────────────────────────────────────────────┐
│  ● 录制  ⟳ 刷新录制  🗑 清除  ⬇ 导入  ⬆ 导出  ⚙ 设置      │
├─────────────────────────────────────────────────────────────┤
│  时间轴概览区 (Overview)                                     │
│  ████████░░░░░████████░░░░░░░░░████████░░░░░              │
├─────────────────────────────────────────────────────────────┤
│  详细时间轴 (Main Thread / Frames / Network...)              │
│  ┌───────────────────────────────────────────────────────┐ │
│  │ Network  ▓▓▓░░░░░░░▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│  │ Frames   ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ ▮ │ │
│  │ Main     ████████████░░░░░████████████░░░░░░░░░░░░░░ │ │
│  │ GPU      ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│  └───────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│  详情面板 (Summary / Bottom-Up / Call Tree / Event Log)      │
└─────────────────────────────────────────────────────────────┘

关键设置选项

// 点击齿轮图标可配置以下选项:

const performanceSettings = {
  // CPU 节流:模拟低端设备
  cpuThrottling: '4x slowdown', // 或 '6x slowdown'
  
  // 网络节流:模拟慢速网络
  networkThrottling: 'Slow 3G',
  
  // 禁用 JavaScript 采样:获取完整调用栈
  disableJavaScriptSamples: false,
  
  // 启用高级绘制工具
  enableAdvancedPaintInstrumentation: true,
  
  // 截图:捕获每帧的页面截图
  screenshots: true,
  
  // 内存:显示内存使用情况
  memory: true
};

录制性能数据

有两种录制方式:

// 方式1:手动录制(适合交互分析)
// 1. 点击录制按钮
// 2. 执行要分析的操作
// 3. 点击停止

// 方式2:刷新录制(适合加载分析)
// 1. 点击刷新录制按钮
// 2. 页面自动刷新并开始录制
// 3. 页面加载完成后自动停止

// 方式3:通过代码触发录制
async function recordPerformance() {
  // 开始录制
  await chrome.devtools.inspectedWindow.eval(
    'console.profile("MyProfile")'
  );
  
  // 执行操作...
  await performHeavyOperation();
  
  // 停止录制
  await chrome.devtools.inspectedWindow.eval(
    'console.profileEnd("MyProfile")'
  );
}

火焰图深度解读

理解火焰图结构

火焰图是性能分析的核心工具,理解它的结构至关重要:

火焰图结构说明:

时间轴 →→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→→

┌─────────────────────────────────────────────────────────┐
│                    Event (click)                         │ ← 顶层:触发事件
├─────────────────────────────────────────────────────────┤
│            Function Call (handleClick)                   │ ← 事件处理函数
├───────────────────────────┬─────────────────────────────┤
│   updateData() - 15ms     │    renderUI() - 25ms        │ ← 子函数调用
├─────────────┬─────────────┼──────────────┬──────────────┤
│ fetch - 10ms│ parse - 5ms │ layout - 10ms│ paint - 15ms │ ← 更深层调用
└─────────────┴─────────────┴──────────────┴──────────────┘

颜色编码:
🟨 黄色 = JavaScript 执行
🟪 紫色 = 布局/回流 (Layout)
🟩 绿色 = 绑制 (Paint)
🟦 蓝色 = 解析 (Parse HTML/CSS)
⬜ 灰色 = 其他/空闲

关键指标:
- 宽度 = 执行时间
- 深度 = 调用层级
- 位置 = 执行顺序

识别性能瓶颈的模式

// 模式1:长任务(Long Task)
// 特征:主线程上超过 50ms 的黄色块
// 影响:阻塞用户交互,导致页面无响应

// 优化前:一个 200ms 的长任务
function processLargeData(data) {
  const result = [];
  for (let i = 0; i < data.length; i++) {
    result.push(heavyCalculation(data[i]));
  }
  return result;
}

// 优化后:分割成多个小任务
async function processLargeDataOptimized(data) {
  const result = [];
  const CHUNK_SIZE = 100;
  
  for (let i = 0; i < data.length; i += CHUNK_SIZE) {
    const chunk = data.slice(i, i + CHUNK_SIZE);
    
    // 每处理一批,让出主线程
    await new Promise(resolve => setTimeout(resolve, 0));
    
    for (const item of chunk) {
      result.push(heavyCalculation(item));
    }
  }
  
  return result;
}
// 模式2:频繁的布局抖动(Layout Thrashing)
// 特征:火焰图中出现多次紫色的 Layout 块
// 影响:强制同步布局,严重影响性能

// 优化前:读写交替导致布局抖动
function updateElements(elements) {
  elements.forEach(el => {
    const height = el.offsetHeight; // 读取 - 触发布局
    el.style.height = height + 10 + 'px'; // 写入 - 使布局失效
  });
}

// 优化后:批量读取,批量写入
function updateElementsOptimized(elements) {
  // 批量读取
  const heights = elements.map(el => el.offsetHeight);
  
  // 批量写入
  elements.forEach((el, i) => {
    el.style.height = heights[i] + 10 + 'px';
  });
}
// 模式3:频繁的垃圾回收(GC)
// 特征:时间轴上频繁出现 Minor GC / Major GC
// 影响:暂停 JavaScript 执行,导致卡顿

// 优化前:频繁创建临时对象
function animate() {
  requestAnimationFrame(() => {
    // 每帧创建新对象 - 产生垃圾
    const position = { x: Math.random() * 100, y: Math.random() * 100 };
    updatePosition(position);
    animate();
  });
}

// 优化后:复用对象
const position = { x: 0, y: 0 }; // 复用对象

function animateOptimized() {
  requestAnimationFrame(() => {
    // 修改现有对象,避免创建新对象
    position.x = Math.random() * 100;
    position.y = Math.random() * 100;
    updatePosition(position);
    animateOptimized();
  });
}

分析面板详解

Summary 面板

Summary 面板提供选中时间范围内的活动总览:

Summary 面板内容:

┌────────────────────────────────────────────┐
│ Total Time: 1523.4 ms                      │
├────────────────────────────────────────────┤
│ Loading:     125.3 ms  ████░░░░░░░  8.2%  │ ← 资源加载时间
│ Scripting:   856.7 ms  ████████████ 56.2% │ ← JavaScript 执行
│ Rendering:   312.4 ms  ██████░░░░░  20.5% │ ← 布局和绘制
│ Painting:    128.6 ms  ███░░░░░░░░  8.4%  │ ← 实际绘制到屏幕
│ System:       67.2 ms  █░░░░░░░░░░  4.4%  │ ← 浏览器内部操作
│ Idle:         33.2 ms  █░░░░░░░░░░  2.2%  │ ← 空闲时间
└────────────────────────────────────────────┘

各阶段含义

const activityTypes = {
  // Loading - 加载阶段
  loading: {
    activities: [
      'Parse HTML',      // 解析 HTML
      'Parse Stylesheet', // 解析 CSS
      'Finish Loading',  // 完成资源加载
      'Receive Data'     // 接收网络数据
    ],
    optimization: '减少资源体积,使用缓存'
  },
  
  // Scripting - 脚本执行
  scripting: {
    activities: [
      'Evaluate Script',     // 评估/执行脚本
      'Compile Script',      // 编译脚本
      'Run Microtasks',      // 执行微任务
      'Event (click, etc.)', // 事件处理
      'Timer Fired',         // 定时器触发
      'XHR Ready State Change' // XHR 状态变化
    ],
    optimization: '代码分割,延迟执行,Web Worker'
  },
  
  // Rendering - 渲染计算
  rendering: {
    activities: [
      'Recalculate Style', // 重新计算样式
      'Layout',            // 布局计算
      'Update Layer Tree', // 更新图层树
      'Pre-Paint'          // 预绑制准备
    ],
    optimization: '减少 DOM 操作,避免强制同步布局'
  },
  
  // Painting - 绘制
  painting: {
    activities: [
      'Paint',             // 绑制
      'Composite Layers',  // 合成图层
      'Rasterize Paint'    // 光栅化
    ],
    optimization: '使用 transform/opacity,减少绑制区域'
  }
};

Bottom-Up 和 Call Tree

这两个面板帮助定位具体的性能热点:

Bottom-Up(自底向上):
- 显示消耗时间最多的函数
- 适合找出"哪个函数最慢"

┌─────────────────────────────────────────────────────────┐
│ Self Time  Total Time  Activity                         │
├─────────────────────────────────────────────────────────┤
│ 245.3 ms   856.7 ms    heavyCalculation                 │
│  89.2 ms   312.4 ms    updateDOM                        │
│  67.8 ms    67.8 ms    JSON.parse                       │
│  45.6 ms   145.2 ms    renderList                       │
└─────────────────────────────────────────────────────────┘

Call Tree(调用树):
- 显示函数调用的层级关系
- 适合理解"执行流程是什么"

┌─────────────────────────────────────────────────────────┐
│ ▼ main                           1523.4 ms             │
│   ▼ handleClick                   856.7 ms             │
│     ▼ processData                 456.3 ms             │
│       ● heavyCalculation          245.3 ms             │
│       ● formatResult              128.4 ms             │
│     ▼ updateUI                    312.4 ms             │
│       ● renderList                145.2 ms             │
│       ● updateDOM                  89.2 ms             │
└─────────────────────────────────────────────────────────┘

实战案例:诊断页面卡顿

案例场景

用户报告:滚动列表页面时出现明显卡顿。

步骤1:录制性能数据

// 录制前的准备
// 1. 清除浏览器缓存
// 2. 关闭其他标签页
// 3. 开启 CPU 4x 节流(模拟低端设备)
// 4. 开始录制 → 滚动页面 → 停止录制

步骤2:分析火焰图

录制结果分析:

时间轴(每帧 16.67ms 目标):
Frame 1: ████████████████████████████████████████████ 52ms ❌
Frame 2: ████████████████████████████████████████████ 48ms ❌
Frame 3: ████████████████████████████████████████████████████ 67ms ❌
Frame 4: ████████████████ 18ms ✓

火焰图详情:
┌──────────────────────────────────────────────────────────┐
│ scroll (Event)                                           │
├──────────────────────────────────────────────────────────┤
│ onScroll                                                 │
├──────────────────────────┬───────────────────────────────┤
│ updateVisibleItems       │ recalculateLayout            │
├──────────────┬───────────┼───────────────────────────────┤
│forEach - 15ms│render-25ms│ Layout (forced) - 35ms       │ ← 问题!
└──────────────┴───────────┴───────────────────────────────┘

发现问题:
1. 每次滚动都触发完整的列表渲染
2. 存在强制同步布局(Layout forced)

步骤3:定位问题代码

// 问题代码
class VirtualList {
  onScroll(event) {
    const items = this.getAllItems();
    
    // 问题1:每次滚动都遍历所有项目
    items.forEach(item => {
      // 问题2:在循环中读取 DOM 属性
      const rect = item.element.getBoundingClientRect();
      
      if (this.isInViewport(rect)) {
        // 问题3:立即修改 DOM
        item.element.style.display = 'block';
        item.element.innerHTML = this.renderItem(item.data);
      } else {
        item.element.style.display = 'none';
      }
    });
  }
}

步骤4:优化代码

// 优化后的代码
class OptimizedVirtualList {
  constructor() {
    this.scrollHandler = this.throttle(this.onScroll.bind(this), 16);
    this.pendingUpdate = null;
  }
  
  // 优化1:节流滚动事件
  throttle(fn, delay) {
    let lastCall = 0;
    return function(...args) {
      const now = Date.now();
      if (now - lastCall >= delay) {
        lastCall = now;
        fn.apply(this, args);
      }
    };
  }
  
  onScroll(event) {
    // 优化2:使用 requestAnimationFrame 批量更新
    if (this.pendingUpdate) {
      cancelAnimationFrame(this.pendingUpdate);
    }
    
    this.pendingUpdate = requestAnimationFrame(() => {
      this.updateVisibleItems();
    });
  }
  
  updateVisibleItems() {
    const scrollTop = this.container.scrollTop;
    const viewportHeight = this.container.clientHeight;
    
    // 优化3:只计算可见范围,不遍历所有项目
    const startIndex = Math.floor(scrollTop / this.itemHeight);
    const endIndex = Math.min(
      startIndex + Math.ceil(viewportHeight / this.itemHeight) + 1,
      this.items.length
    );
    
    // 优化4:使用 DocumentFragment 批量 DOM 操作
    const fragment = document.createDocumentFragment();
    
    for (let i = startIndex; i < endIndex; i++) {
      const element = this.getOrCreateElement(i);
      element.style.transform = `translateY(${i * this.itemHeight}px)`;
      fragment.appendChild(element);
    }
    
    // 优化5:一次性更新 DOM
    this.contentContainer.innerHTML = '';
    this.contentContainer.appendChild(fragment);
  }
  
  // 优化6:对象池复用 DOM 元素
  getOrCreateElement(index) {
    if (this.elementPool.length > 0) {
      const element = this.elementPool.pop();
      this.updateElement(element, this.items[index]);
      return element;
    }
    return this.createElement(this.items[index]);
  }
}

步骤5:验证优化效果

优化后录制结果:

时间轴:
Frame 1: ████████ 12ms ✓
Frame 2: ██████████ 14ms ✓
Frame 3: ████████ 11ms ✓
Frame 4: ██████████ 15ms ✓

性能提升:
- 平均帧时间:52ms → 13ms(提升 75%)
- 帧率:19 FPS → 60 FPS
- Layout 时间:35ms → 2ms

高级技巧

使用 Performance API 程序化分析

// 创建性能测量工具类
class PerformanceAnalyzer {
  constructor() {
    this.marks = new Map();
  }
  
  // 标记开始点
  start(name) {
    performance.mark(`${name}-start`);
    this.marks.set(name, performance.now());
  }
  
  // 标记结束点并计算耗时
  end(name) {
    performance.mark(`${name}-end`);
    performance.measure(name, `${name}-start`, `${name}-end`);
    
    const startTime = this.marks.get(name);
    const duration = performance.now() - startTime;
    
    this.marks.delete(name);
    
    return {
      name,
      duration,
      timestamp: Date.now()
    };
  }
  
  // 获取所有测量结果
  getEntries(name) {
    return performance.getEntriesByName(name);
  }
  
  // 清理测量数据
  clear() {
    performance.clearMarks();
    performance.clearMeasures();
    this.marks.clear();
  }
  
  // 分析资源加载性能
  analyzeResources() {
    const resources = performance.getEntriesByType('resource');
    
    return resources.map(resource => ({
      name: resource.name,
      type: resource.initiatorType,
      duration: resource.duration,
      transferSize: resource.transferSize,
      // 关键时间点
      timing: {
        dns: resource.domainLookupEnd - resource.domainLookupStart,
        tcp: resource.connectEnd - resource.connectStart,
        ttfb: resource.responseStart - resource.requestStart,
        download: resource.responseEnd - resource.responseStart
      }
    }));
  }
  
  // 分析长任务
  observeLongTasks(callback) {
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        callback({
          duration: entry.duration,
          startTime: entry.startTime,
          attribution: entry.attribution
        });
      }
    });
    
    observer.observe({ entryTypes: ['longtask'] });
    return observer;
  }
}

// 使用示例
const analyzer = new PerformanceAnalyzer();

// 测量函数执行时间
analyzer.start('dataProcessing');
await processLargeDataset();
const result = analyzer.end('dataProcessing');
console.log(`数据处理耗时: ${result.duration.toFixed(2)}ms`);

// 监控长任务
analyzer.observeLongTasks((task) => {
  console.warn(`检测到长任务: ${task.duration.toFixed(2)}ms`);
});

自动化性能测试

// 使用 Puppeteer 进行自动化性能测试
const puppeteer = require('puppeteer');

async function runPerformanceTest(url) {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  // 启用性能追踪
  await page.tracing.start({
    path: 'trace.json',
    screenshots: true
  });
  
  // 模拟移动设备
  await page.emulate(puppeteer.devices['iPhone 12']);
  
  // 设置 CPU 节流
  const client = await page.target().createCDPSession();
  await client.send('Emulation.setCPUThrottlingRate', { rate: 4 });
  
  // 收集性能指标
  await page.goto(url, { waitUntil: 'networkidle0' });
  
  const metrics = await page.metrics();
  const performanceTiming = await page.evaluate(() => {
    const timing = performance.timing;
    return {
      // 页面加载时间
      pageLoad: timing.loadEventEnd - timing.navigationStart,
      // DOM 解析时间
      domParse: timing.domComplete - timing.domLoading,
      // 首字节时间
      ttfb: timing.responseStart - timing.requestStart,
      // DOM 可交互时间
      domInteractive: timing.domInteractive - timing.navigationStart
    };
  });
  
  // 停止追踪
  await page.tracing.stop();
  
  await browser.close();
  
  return {
    metrics,
    performanceTiming,
    traceFile: 'trace.json'
  };
}

// 性能基准测试
async function benchmarkPage(url, runs = 5) {
  const results = [];
  
  for (let i = 0; i < runs; i++) {
    const result = await runPerformanceTest(url);
    results.push(result.performanceTiming);
  }
  
  // 计算平均值
  const average = {
    pageLoad: avg(results.map(r => r.pageLoad)),
    domParse: avg(results.map(r => r.domParse)),
    ttfb: avg(results.map(r => r.ttfb)),
    domInteractive: avg(results.map(r => r.domInteractive))
  };
  
  return {
    runs,
    results,
    average,
    // 性能评级
    grade: getPerformanceGrade(average)
  };
}

function avg(arr) {
  return arr.reduce((a, b) => a + b, 0) / arr.length;
}

function getPerformanceGrade(timing) {
  if (timing.pageLoad < 1000) return 'A';
  if (timing.pageLoad < 2500) return 'B';
  if (timing.pageLoad < 4000) return 'C';
  return 'D';
}

性能分析报告生成

// 生成性能分析报告
class PerformanceReporter {
  constructor() {
    this.data = {
      timestamp: new Date().toISOString(),
      metrics: {},
      issues: [],
      recommendations: []
    };
  }
  
  // 收集页面性能数据
  async collectData() {
    // Core Web Vitals
    this.data.metrics.webVitals = await this.getWebVitals();
    
    // 资源加载分析
    this.data.metrics.resources = this.analyzeResources();
    
    // JavaScript 执行分析
    this.data.metrics.javascript = await this.analyzeJavaScript();
    
    // 识别问题
    this.identifyIssues();
    
    // 生成建议
    this.generateRecommendations();
    
    return this.data;
  }
  
  async getWebVitals() {
    return new Promise((resolve) => {
      const vitals = {};
      
      // LCP
      new PerformanceObserver((list) => {
        const entries = list.getEntries();
        vitals.lcp = entries[entries.length - 1].startTime;
      }).observe({ entryTypes: ['largest-contentful-paint'] });
      
      // FID
      new PerformanceObserver((list) => {
        vitals.fid = list.getEntries()[0].processingStart - 
                     list.getEntries()[0].startTime;
      }).observe({ entryTypes: ['first-input'] });
      
      // CLS
      let clsValue = 0;
      new PerformanceObserver((list) => {
        for (const entry of list.getEntries()) {
          if (!entry.hadRecentInput) {
            clsValue += entry.value;
          }
        }
        vitals.cls = clsValue;
      }).observe({ entryTypes: ['layout-shift'] });
      
      // 等待收集
      setTimeout(() => resolve(vitals), 5000);
    });
  }
  
  analyzeResources() {
    const resources = performance.getEntriesByType('resource');
    
    // 按类型分组
    const byType = {};
    resources.forEach(r => {
      const type = r.initiatorType;
      if (!byType[type]) {
        byType[type] = { count: 0, totalSize: 0, totalTime: 0 };
      }
      byType[type].count++;
      byType[type].totalSize += r.transferSize || 0;
      byType[type].totalTime += r.duration;
    });
    
    // 找出最慢的资源
    const slowest = [...resources]
      .sort((a, b) => b.duration - a.duration)
      .slice(0, 5);
    
    return { byType, slowest };
  }
  
  identifyIssues() {
    const { webVitals, resources } = this.data.metrics;
    
    // LCP 问题
    if (webVitals.lcp > 2500) {
      this.data.issues.push({
        severity: webVitals.lcp > 4000 ? 'critical' : 'warning',
        type: 'LCP',
        message: `LCP 为 ${webVitals.lcp.toFixed(0)}ms,超过 2.5s 阈值`,
        value: webVitals.lcp
      });
    }
    
    // FID 问题
    if (webVitals.fid > 100) {
      this.data.issues.push({
        severity: webVitals.fid > 300 ? 'critical' : 'warning',
        type: 'FID',
        message: `FID 为 ${webVitals.fid.toFixed(0)}ms,超过 100ms 阈值`,
        value: webVitals.fid
      });
    }
    
    // 大资源问题
    resources.slowest.forEach(r => {
      if (r.duration > 1000) {
        this.data.issues.push({
          severity: 'warning',
          type: 'SlowResource',
          message: `资源加载过慢: ${r.name.split('/').pop()}`,
          value: r.duration
        });
      }
    });
  }
  
  generateRecommendations() {
    this.data.issues.forEach(issue => {
      switch (issue.type) {
        case 'LCP':
          this.data.recommendations.push(
            '优化最大内容绘制(LCP):预加载关键资源、优化服务器响应时间、使用 CDN'
          );
          break;
        case 'FID':
          this.data.recommendations.push(
            '减少首次输入延迟(FID):分割长任务、延迟非关键 JavaScript、使用 Web Worker'
          );
          break;
        case 'SlowResource':
          this.data.recommendations.push(
            '优化慢资源:启用压缩、使用缓存、考虑懒加载'
          );
          break;
      }
    });
  }
  
  // 生成 HTML 报告
  toHTML() {
    return `
      <!DOCTYPE html>
      <html>
      <head>
        <title>性能分析报告</title>
        <style>
          body { font-family: system-ui; max-width: 800px; margin: 0 auto; padding: 20px; }
          .metric { padding: 10px; margin: 10px 0; border-radius: 8px; }
          .good { background: #d4edda; }
          .warning { background: #fff3cd; }
          .critical { background: #f8d7da; }
        </style>
      </head>
      <body>
        <h1>性能分析报告</h1>
        <p>生成时间: ${this.data.timestamp}</p>
        
        <h2>Core Web Vitals</h2>
        ${this.renderWebVitals()}
        
        <h2>发现的问题</h2>
        ${this.renderIssues()}
        
        <h2>优化建议</h2>
        <ul>
          ${this.data.recommendations.map(r => `<li>${r}</li>`).join('')}
        </ul>
      </body>
      </html>
    `;
  }
  
  renderWebVitals() {
    const { webVitals } = this.data.metrics;
    return `
      <div class="metric ${webVitals.lcp <= 2500 ? 'good' : webVitals.lcp <= 4000 ? 'warning' : 'critical'}">
        <strong>LCP:</strong> ${webVitals.lcp?.toFixed(0) || 'N/A'}ms
      </div>
      <div class="metric ${webVitals.fid <= 100 ? 'good' : webVitals.fid <= 300 ? 'warning' : 'critical'}">
        <strong>FID:</strong> ${webVitals.fid?.toFixed(0) || 'N/A'}ms
      </div>
      <div class="metric ${webVitals.cls <= 0.1 ? 'good' : webVitals.cls <= 0.25 ? 'warning' : 'critical'}">
        <strong>CLS:</strong> ${webVitals.cls?.toFixed(3) || 'N/A'}
      </div>
    `;
  }
  
  renderIssues() {
    return this.data.issues.map(issue => `
      <div class="metric ${issue.severity}">
        <strong>${issue.type}:</strong> ${issue.message}
      </div>
    `).join('');
  }
}

常见问题与解答

Q1: 火焰图中出现 "Forced reflow" 是什么意思?

// Forced reflow(强制回流)发生在以下情况:
// 在修改 DOM 后立即读取布局属性

// 触发强制回流的属性读取:
const layoutTriggers = [
  'offsetTop', 'offsetLeft', 'offsetWidth', 'offsetHeight',
  'scrollTop', 'scrollLeft', 'scrollWidth', 'scrollHeight',
  'clientTop', 'clientLeft', 'clientWidth', 'clientHeight',
  'getComputedStyle()', 'getBoundingClientRect()'
];

// 错误示例
element.style.width = '100px';
console.log(element.offsetWidth); // 触发强制回流!

// 正确做法
const width = element.offsetWidth; // 先读取
element.style.width = '100px';     // 后写入

Q2: 如何区分 "self time" 和 "total time"?

Self Time vs Total Time:

函数 A(total: 100ms)
├── 自身代码执行:30ms  ← Self Time
└── 调用函数 B:70ms
    └── 函数 B(total: 70ms)
        ├── 自身代码:50ms  ← B 的 Self Time
        └── 调用函数 C:20ms
            └── 函数 C(total: 20ms, self: 20ms)

分析时:
- 关注 Self Time 高的函数 = 找到性能热点
- 关注 Total Time 高的调用链 = 理解性能消耗的来源

Q3: 如何判断是 CPU 瓶颈还是 I/O 瓶颈?

CPU 瓶颈特征:
- 火焰图中黄色(JavaScript)块占比高
- 主线程几乎没有空闲时间
- FPS 持续低于 60

I/O 瓶颈特征:
- 火焰图中有大量等待时间(灰色间隙)
- Network 面板显示资源加载慢
- 主线程有空闲但页面仍然卡顿

解决方案:
CPU 瓶颈 → 优化代码、使用 Web Worker、延迟非关键脚本
I/O 瓶颈 → 预加载、缓存、CDN、资源压缩

最佳实践总结

性能分析最佳实践清单:

录制准备:
✓ 使用隐身模式排除插件干扰
✓ 关闭其他标签页
✓ 开启 CPU/网络节流模拟真实环境
✓ 录制多次取平均值

分析技巧:
✓ 先看 Summary 了解时间分布
✓ 用 Bottom-Up 找性能热点函数
✓ 用 Call Tree 理解调用链
✓ 关注长任务(>50ms)和强制回流

常见优化方向:
✓ JavaScript:分割长任务、延迟执行、Web Worker
✓ Layout:批量 DOM 操作、避免布局抖动
✓ Paint:使用 transform 代替 top/left
✓ 资源:预加载、缓存、压缩、CDN

通过系统地使用 Chrome DevTools Performance 面板,你可以从"感觉页面慢"升级到"精确知道哪里慢、为什么慢、如何优化"。这是每个前端开发者的必备技能。