阿里云主机折上折
  • 微信号
Current Site:Index > Comprehensive performance analysis with Lighthouse

Comprehensive performance analysis with Lighthouse

Author:Chuan Chen 阅读数:3817人阅读 分类: 性能优化

Introduction to Lighthouse

Lighthouse is an open-source automated tool developed by Google to improve web page quality. It can audit various aspects of a webpage, including performance, accessibility, progressive web apps, and SEO. Run via Chrome extensions, Chrome DevTools, or the command line, Lighthouse generates a detailed report with scores for each metric and improvement suggestions.

Performance Metrics Breakdown

Lighthouse's performance score is based on multiple key metrics, each reflecting different aspects of user experience:

  1. First Contentful Paint (FCP) Measures the time from when the page starts loading to when any part of the page content is rendered on the screen. For users, this is the first feedback they perceive about the page loading.

    // Use PerformanceObserver to monitor 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) Measures when the largest content element in the viewport becomes visible. This typically corresponds to when the page's main content finishes loading.

  3. First Input Delay (FID) Measures the time from when a user first interacts with the page to when the browser can actually respond to that interaction.

  4. Cumulative Layout Shift (CLS) Measures the visual stability of a page during loading, quantifying unexpected layout shifts.

  5. Speed Index Measures how quickly the page content is visually populated.

Performance Optimization Practices

Optimizing Resource Loading

  1. Code Splitting and Lazy Loading Use dynamic imports to split code and load modules only when needed:

    // Dynamic import example
    document.getElementById('btn').addEventListener('click', async () => {
      const module = await import('./expensiveModule.js');
      module.doSomething();
    });
    

    For resources like images, use lazy loading:

    <img src="placeholder.jpg" data-src="actual-image.jpg" loading="lazy" alt="Example image">
    
  2. Preloading Critical Resources Use <link rel="preload"> to load critical resources early:

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

Optimizing JavaScript Execution

  1. Reducing Main Thread Work Break long-running tasks into smaller chunks:

    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. Using Web Workers Move computation-intensive tasks to Web Workers:

    // Main thread
    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);
    };
    

Optimizing CSS

  1. Reducing Critical CSS Extract and inline CSS required for above-the-fold rendering:

    <style>
      /* Critical CSS */
      .header, .hero { ... }
    </style>
    
  2. Avoiding CSS @import @import blocks parallel loading; use multiple <link> tags instead.

Cache Strategy Optimization

Server-Side Caching

Set appropriate HTTP cache headers:

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

Client-Side Caching

Implement offline caching with 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))
  );
});

Rendering Performance Optimization

Reducing Repaints and Reflows

  1. Using transform and opacity for Animations These properties don't trigger layout or paint:

    .animate {
      transition: transform 0.3s ease;
    }
    .animate:hover {
      transform: scale(1.1);
    }
    
  2. Using will-change to Hint the Browser Inform the browser in advance about elements that will change:

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

Virtual Scrolling

Implement virtual scrolling for long lists:

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();
    }
  }
}

Monitoring and Continuous Optimization

Using Lighthouse CI

Integrate Lighthouse CI into the build process:

# .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

Performance Budgets

Create a performance budget file:

{
  "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}]
      }
    }
  }
}

Advanced Optimization Techniques

Using the PRPL Pattern

PRPL (Push, Render, Pre-cache, Lazy-load) is a performance optimization pattern:

  1. Push critical resources
  2. Render the initial route
  3. Pre-cache remaining routes
  4. Lazy-load non-critical resources

Server Push

Use HTTP/2 server push:

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

Using Brotli Compression

Configure the server to use Brotli compression:

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

Common Issues and Solutions

CLS Issues

Problem: Page elements shift unexpectedly during loading.

Solutions:

  • Set width and height attributes for images and videos
  • Reserve space for ads
  • Avoid inserting new content above existing content
<img src="image.jpg" width="300" height="200" alt="Example">

LCP Issues

Problem: Main content loads too slowly.

Solutions:

  • Preload LCP elements
  • Use <link rel="preconnect"> to establish early connections
  • Optimize server response time
<link rel="preconnect" href="https://example.com">
<link rel="preload" as="image" href="hero-image.jpg">

TTI Issues

Problem: Page takes too long to become interactive.

Solutions:

  • Code splitting
  • Reduce third-party scripts
  • Defer non-critical JavaScript
// Defer loading non-critical scripts
window.addEventListener('load', () => {
  const script = document.createElement('script');
  script.src = 'non-critical.js';
  document.body.appendChild(script);
});

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

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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.