阿里云主机折上折
  • 微信号
您当前的位置:网站首页 > Lighthouse全面性能分析

Lighthouse全面性能分析

作者:陈川 阅读数:3816人阅读 分类: 性能优化

Lighthouse 简介

Lighthouse 是 Google 开发的开源自动化工具,用于改进网页质量。它可以对网页的性能、可访问性、渐进式 Web 应用、SEO 等方面进行审核。通过 Chrome 扩展程序、Chrome DevTools 或命令行运行,Lighthouse 会生成一份详细的报告,其中包含各项指标的评分和改进建议。

性能指标解析

Lighthouse 的性能评分基于多个关键指标,每个指标都反映了用户体验的不同方面:

  1. 首次内容绘制 (First Contentful Paint, FCP) 测量页面从开始加载到页面内容的任何部分在屏幕上完成渲染的时间。对于用户来说,这是他们感知页面加载的第一个反馈。

    // 使用 PerformanceObserver 监听 FCP
    const observer = new PerformanceObserver((list) => {
      for (const entry of list.getEntries()) {
        if (entry.name === 'first-contentful-paint') {
          console.log('FCP:', entry.startTime);
          observer.disconnect();
        }
      }
    });
    observer.observe({type: 'paint', buffered: true});
    
  2. 最大内容绘制 (Largest Contentful Paint, LCP) 测量视口中最大的内容元素何时可见。这通常对应于页面的主要内容加载完成的时间。

  3. 首次输入延迟 (First Input Delay, FID) 测量从用户首次与页面交互到浏览器实际能够响应该交互的时间。

  4. 累积布局偏移 (Cumulative Layout Shift, CLS) 测量页面在加载期间视觉稳定性的指标,量化了意外的布局偏移量。

  5. 速度指数 (Speed Index) 测量页面内容视觉填充的速度。

性能优化实践

优化资源加载

  1. 代码分割与懒加载 使用动态导入拆分代码,只在需要时加载模块:

    // 动态导入示例
    document.getElementById('btn').addEventListener('click', async () => {
      const module = await import('./expensiveModule.js');
      module.doSomething();
    });
    

    对于图片等资源,使用懒加载:

    <img src="placeholder.jpg" data-src="actual-image.jpg" loading="lazy" alt="示例图片">
    
  2. 预加载关键资源 使用 <link rel="preload"> 提前加载关键资源:

    <link rel="preload" href="critical.css" as="style">
    <link rel="preload" href="main.js" as="script">
    

优化 JavaScript 执行

  1. 减少主线程工作 将长时间运行的任务分解为小块:

    function processInChunks(array, chunkSize, callback) {
      let index = 0;
      function nextChunk() {
        const chunk = array.slice(index, index + chunkSize);
        if (chunk.length === 0) return;
        callback(chunk);
        index += chunkSize;
        requestIdleCallback(nextChunk);
      }
      nextChunk();
    }
    
  2. 使用 Web Workers 将计算密集型任务移到 Web Worker 中:

    // 主线程
    const worker = new Worker('worker.js');
    worker.postMessage(data);
    worker.onmessage = (e) => {
      console.log('Result:', e.data);
    };
    
    // worker.js
    self.onmessage = (e) => {
      const result = heavyComputation(e.data);
      self.postMessage(result);
    };
    

优化 CSS

  1. 减少关键 CSS 提取首屏渲染所需的 CSS 并内联:

    <style>
      /* 关键CSS */
      .header, .hero { ... }
    </style>
    
  2. 避免 CSS @import @import 会阻止并行加载,改用多个 <link> 标签。

缓存策略优化

服务端缓存

设置适当的 HTTP 缓存头:

Cache-Control: public, max-age=31536000, immutable

客户端缓存

使用 Service Worker 实现离线缓存:

// service-worker.js
const CACHE_NAME = 'v1';
const ASSETS = [
  '/',
  '/styles/main.css',
  '/scripts/app.js'
];

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => cache.addAll(ASSETS))
  );
});

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request)
      .then(response => response || fetch(event.request))
  );
});

渲染性能优化

减少重绘和回流

  1. 使用 transform 和 opacity 实现动画 这些属性不会触发布局或绘制:

    .animate {
      transition: transform 0.3s ease;
    }
    .animate:hover {
      transform: scale(1.1);
    }
    
  2. 使用 will-change 提示浏览器 提前告知浏览器哪些元素会变化:

    .will-change {
      will-change: transform, opacity;
    }
    

虚拟滚动

对于长列表,实现虚拟滚动:

class VirtualScroll {
  constructor(container, items, itemHeight, renderItem) {
    this.container = container;
    this.items = items;
    this.itemHeight = itemHeight;
    this.renderItem = renderItem;
    
    this.visibleCount = Math.ceil(container.clientHeight / itemHeight);
    this.startIndex = 0;
    
    container.style.position = 'relative';
    this.content = document.createElement('div');
    this.content.style.height = `${items.length * itemHeight}px`;
    container.appendChild(this.content);
    
    this.render();
    container.addEventListener('scroll', () => this.handleScroll());
  }
  
  render() {
    const endIndex = Math.min(this.startIndex + this.visibleCount, this.items.length);
    let html = '';
    for (let i = this.startIndex; i < endIndex; i++) {
      html += this.renderItem(this.items[i], i);
    }
    this.content.innerHTML = html;
    this.content.style.transform = `translateY(${this.startIndex * this.itemHeight}px)`;
  }
  
  handleScroll() {
    const scrollTop = this.container.scrollTop;
    const newStartIndex = Math.floor(scrollTop / this.itemHeight);
    if (newStartIndex !== this.startIndex) {
      this.startIndex = newStartIndex;
      this.render();
    }
  }
}

监控与持续优化

使用 Lighthouse CI

集成 Lighthouse CI 到构建流程中:

# .github/workflows/lighthouse.yml
name: Lighthouse
on: [push]
jobs:
  lighthouse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
      - run: npm install
      - run: npm run build
      - uses: treosh/lighthouse-ci-action@v8
        with:
          urls: |
            http://localhost:3000
          budgetPath: ./lighthouse-budget.json

性能预算

创建性能预算文件:

{
  "ci": {
    "collect": {
      "numberOfRuns": 3,
      "url": ["http://localhost:3000"]
    },
    "assert": {
      "assertions": {
        "first-contentful-paint": ["error", {"maxNumericValue": 2000}],
        "largest-contentful-paint": ["error", {"maxNumericValue": 4000}],
        "cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
        "interactive": ["error", {"maxNumericValue": 5000}]
      }
    }
  }
}

高级优化技巧

使用 PRPL 模式

PRPL (Push, Render, Pre-cache, Lazy-load) 是一种性能优化模式:

  1. Push 关键资源
  2. Render 初始路由
  3. Pre-cache 剩余路由
  4. Lazy-load 非关键资源

服务器推送

使用 HTTP/2 服务器推送:

Link: </styles/main.css>; rel=preload; as=style

使用 Brotli 压缩

配置服务器使用 Brotli 压缩:

# nginx 配置
brotli on;
brotli_comp_level 6;
brotli_types text/plain text/css application/javascript application/json image/svg+xml;

常见问题与解决方案

CLS 问题

问题:页面元素在加载过程中发生意外移动。

解决方案

  • 为图片和视频设置尺寸属性
  • 预留广告位空间
  • 避免在现有内容上方插入新内容
<img src="image.jpg" width="300" height="200" alt="示例">

LCP 问题

问题:主要内容加载过慢。

解决方案

  • 预加载 LCP 元素
  • 使用 <link rel="preconnect"> 提前建立连接
  • 优化服务器响应时间
<link rel="preconnect" href="https://example.com">
<link rel="preload" as="image" href="hero-image.jpg">

TTI 问题

问题:页面可交互时间过长。

解决方案

  • 代码分割
  • 减少第三方脚本
  • 延迟非关键 JavaScript
// 延迟加载非关键脚本
window.addEventListener('load', () => {
  const script = document.createElement('script');
  script.src = 'non-critical.js';
  document.body.appendChild(script);
});

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

前端川

前端川,陈川的代码茶馆🍵,专治各种不服的Bug退散符💻,日常贩卖秃头警告级的开发心得🛠️,附赠一行代码笑十年的摸鱼宝典🐟,偶尔掉落咖啡杯里泡开的像素级浪漫☕。‌