performance

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Performance optimization

网页性能优化

Deep performance optimization based on Lighthouse performance audits. Focuses on loading speed, runtime efficiency, and resource optimization.
基于Lighthouse性能审计的深度性能优化,重点关注加载速度、运行时效率和资源优化。

How it works

工作原理

  1. Identify performance bottlenecks in code and assets
  2. Prioritize by impact on Core Web Vitals
  3. Provide specific optimizations with code examples
  4. Measure improvement with before/after metrics
  1. 识别代码和资源中的性能瓶颈
  2. 根据对Core Web Vitals的影响优先级排序
  3. 提供带有代码示例的具体优化方案
  4. 通过优化前后的指标衡量改进效果

Performance budget

性能预算

ResourceBudgetRationale
Total page weight< 1.5 MB3G loads in ~4s
JavaScript (compressed)< 300 KBParsing + execution time
CSS (compressed)< 100 KBRender blocking
Images (above-fold)< 500 KBLCP impact
Fonts< 100 KBFOIT/FOUT prevention
Third-party< 200 KBUncontrolled latency
资源预算依据
页面总大小< 1.5 MB3G网络下约4秒加载完成
JavaScript(压缩后)< 300 KB解析与执行时间控制
CSS(压缩后)< 100 KB避免渲染阻塞
首屏图片< 500 KB影响LCP指标
字体< 100 KB防止FOIT/FOUT问题
第三方资源< 200 KB控制不可控延迟

Critical rendering path

关键渲染路径

Server response

服务器响应

  • TTFB < 800ms. Time to First Byte should be fast. Use CDN, caching, and efficient backends.
  • Enable compression. Gzip or Brotli for text assets. Brotli preferred (15-20% smaller).
  • HTTP/2 or HTTP/3. Multiplexing reduces connection overhead.
  • Edge caching. Cache HTML at CDN edge when possible.
  • TTFB < 800ms. 首字节响应时间应尽可能快。使用CDN、缓存和高效后端服务。
  • 启用压缩. 对文本资源使用Gzip或Brotli压缩。优先选择Brotli(压缩率高15-20%)。
  • HTTP/2或HTTP/3. 多路复用减少连接开销。
  • 边缘缓存. 尽可能在CDN边缘缓存HTML内容。

Resource loading

资源加载

Preconnect to required origins:
html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
Preload critical resources:
html
<!-- LCP image -->
<link rel="preload" href="/hero.webp" as="image" fetchpriority="high">

<!-- Critical font -->
<link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossorigin>
Defer non-critical CSS:
html
<!-- Critical CSS inlined -->
<style>/* Above-fold styles */</style>

<!-- Non-critical CSS -->
<link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/styles.css"></noscript>
预连接到必要源:
html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>
预加载关键资源:
html
<!-- LCP图片 -->
<link rel="preload" href="/hero.webp" as="image" fetchpriority="high">

<!-- 关键字体 -->
<link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossorigin>
延迟非关键CSS:
html
<!-- 内联关键CSS -->
<style>/* 首屏样式 */</style>

<!-- 非关键CSS -->
<link rel="preload" href="/styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/styles.css"></noscript>

JavaScript optimization

JavaScript优化

Defer non-essential scripts:
html
<!-- Parser-blocking (avoid) -->
<script src="/critical.js"></script>

<!-- Deferred (preferred) -->
<script defer src="/app.js"></script>

<!-- Async (for independent scripts) -->
<script async src="/analytics.js"></script>

<!-- Module (deferred by default) -->
<script type="module" src="/app.mjs"></script>
Code splitting patterns:
javascript
// Route-based splitting
const Dashboard = lazy(() => import('./Dashboard'));

// Component-based splitting
const HeavyChart = lazy(() => import('./HeavyChart'));

// Feature-based splitting
if (user.isPremium) {
  const PremiumFeatures = await import('./PremiumFeatures');
}
Tree shaking best practices:
javascript
// ❌ Imports entire library
import _ from 'lodash';
_.debounce(fn, 300);

// ✅ Imports only what's needed
import debounce from 'lodash/debounce';
debounce(fn, 300);
延迟非必要脚本:
html
<!-- 阻塞解析(应避免) -->
<script src="/critical.js"></script>

<!-- 延迟加载(推荐) -->
<script defer src="/app.js"></script>

<!-- 异步加载(适用于独立脚本) -->
<script async src="/analytics.js"></script>

<!-- 模块脚本(默认延迟) -->
<script type="module" src="/app.mjs"></script>
代码分割模式:
javascript
// 基于路由分割
const Dashboard = lazy(() => import('./Dashboard'));

// 基于组件分割
const HeavyChart = lazy(() => import('./HeavyChart'));

// 基于功能分割
if (user.isPremium) {
  const PremiumFeatures = await import('./PremiumFeatures');
}
Tree Shaking最佳实践:
javascript
// ❌ 导入整个库
import _ from 'lodash';
_.debounce(fn, 300);

// ✅ 仅导入所需部分
import debounce from 'lodash/debounce';
debounce(fn, 300);

Image optimization

图片优化

Format selection

格式选择

FormatUse caseBrowser support
AVIFPhotos, best compression92%+
WebPPhotos, good fallback97%+
PNGGraphics with transparencyUniversal
SVGIcons, logos, illustrationsUniversal
格式使用场景浏览器支持
AVIF照片,最优压缩率92%+
WebP照片,良好降级方案97%+
PNG带透明度的图形全兼容
SVG图标、logo、插画全兼容

Responsive images

响应式图片

html
<picture>
  <!-- AVIF for modern browsers -->
  <source 
    type="image/avif"
    srcset="hero-400.avif 400w,
            hero-800.avif 800w,
            hero-1200.avif 1200w"
    sizes="(max-width: 600px) 100vw, 50vw">
  
  <!-- WebP fallback -->
  <source 
    type="image/webp"
    srcset="hero-400.webp 400w,
            hero-800.webp 800w,
            hero-1200.webp 1200w"
    sizes="(max-width: 600px) 100vw, 50vw">
  
  <!-- JPEG fallback -->
  <img 
    src="hero-800.jpg"
    srcset="hero-400.jpg 400w,
            hero-800.jpg 800w,
            hero-1200.jpg 1200w"
    sizes="(max-width: 600px) 100vw, 50vw"
    width="1200" 
    height="600"
    alt="Hero image"
    loading="lazy"
    decoding="async">
</picture>
html
<picture>
  <!-- 现代浏览器使用AVIF -->
  <source 
    type="image/avif"
    srcset="hero-400.avif 400w,
            hero-800.avif 800w,
            hero-1200.avif 1200w"
    sizes="(max-width: 600px) 100vw, 50vw">
  
  <!-- 降级为WebP -->
  <source 
    type="image/webp"
    srcset="hero-400.webp 400w,
            hero-800.webp 800w,
            hero-1200.webp 1200w"
    sizes="(max-width: 600px) 100vw, 50vw">
  
  <!-- 最终降级为JPEG -->
  <img 
    src="hero-800.jpg"
    srcset="hero-400.jpg 400w,
            hero-800.jpg 800w,
            hero-1200.jpg 1200w"
    sizes="(max-width: 600px) 100vw, 50vw"
    width="1200" 
    height="600"
    alt="首页主图"
    loading="lazy"
    decoding="async">
</picture>

LCP image priority

LCP图片优先级

html
<!-- Above-fold LCP image: eager loading, high priority -->
<img 
  src="hero.webp" 
  fetchpriority="high"
  loading="eager"
  decoding="sync"
  alt="Hero">

<!-- Below-fold images: lazy loading -->
<img 
  src="product.webp" 
  loading="lazy"
  decoding="async"
  alt="Product">
html
<!-- 首屏LCP图片:立即加载,高优先级 -->
<img 
  src="hero.webp" 
  fetchpriority="high"
  loading="eager"
  decoding="sync"
  alt="首页主图">

<!-- 首屏以下图片:懒加载 -->
<img 
  src="product.webp" 
  loading="lazy"
  decoding="async"
  alt="产品图片">

Font optimization

字体优化

Loading strategy

加载策略

css
/* System font stack as fallback */
body {
  font-family: 'Custom Font', -apple-system, BlinkMacSystemFont, 
               'Segoe UI', Roboto, sans-serif;
}

/* Prevent invisible text */
@font-face {
  font-family: 'Custom Font';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap; /* or optional for non-critical */
  font-weight: 400;
  font-style: normal;
  unicode-range: U+0000-00FF; /* Subset to Latin */
}
css
/* 使用系统字体栈作为降级方案 */
body {
  font-family: 'Custom Font', -apple-system, BlinkMacSystemFont, 
               'Segoe UI', Roboto, sans-serif;
}

/* 避免文本不可见 */
@font-face {
  font-family: 'Custom Font';
  src: url('/fonts/custom.woff2') format('woff2');
  font-display: swap; /* 非关键字体可使用optional */
  font-weight: 400;
  font-style: normal;
  unicode-range: U+0000-00FF; /* 子集化为拉丁语系 */
}

Preloading critical fonts

预加载关键字体

html
<link rel="preload" href="/fonts/heading.woff2" as="font" type="font/woff2" crossorigin>
html
<link rel="preload" href="/fonts/heading.woff2" as="font" type="font/woff2" crossorigin>

Variable fonts

可变字体

css
/* One file instead of multiple weights */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter-Variable.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-display: swap;
}
css
/* 单个文件替代多个字重文件 */
@font-face {
  font-family: 'Inter';
  src: url('/fonts/Inter-Variable.woff2') format('woff2-variations');
  font-weight: 100 900;
  font-display: swap;
}

Caching strategy

缓存策略

Cache-Control headers

Cache-Control响应头

undefined
undefined

HTML (short or no cache)

HTML(短缓存或无缓存)

Cache-Control: no-cache, must-revalidate
Cache-Control: no-cache, must-revalidate

Static assets with hash (immutable)

带哈希的静态资源(不可变)

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

Static assets without hash

无哈希的静态资源

Cache-Control: public, max-age=86400, stale-while-revalidate=604800
Cache-Control: public, max-age=86400, stale-while-revalidate=604800

API responses

API响应

Cache-Control: private, max-age=0, must-revalidate
undefined
Cache-Control: private, max-age=0, must-revalidate
undefined

Service worker caching

Service Worker缓存

javascript
// Cache-first for static assets
self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'image' ||
      event.request.destination === 'style' ||
      event.request.destination === 'script') {
    event.respondWith(
      caches.match(event.request).then((cached) => {
        return cached || fetch(event.request).then((response) => {
          const clone = response.clone();
          caches.open('static-v1').then((cache) => cache.put(event.request, clone));
          return response;
        });
      })
    );
  }
});
javascript
// 静态资源优先使用缓存
self.addEventListener('fetch', (event) => {
  if (event.request.destination === 'image' ||
      event.request.destination === 'style' ||
      event.request.destination === 'script') {
    event.respondWith(
      caches.match(event.request).then((cached) => {
        return cached || fetch(event.request).then((response) => {
          const clone = response.clone();
          caches.open('static-v1').then((cache) => cache.put(event.request, clone));
          return response;
        });
      })
    );
  }
});

Runtime performance

运行时性能

Avoid layout thrashing

避免布局抖动

javascript
// ❌ Forces multiple reflows
elements.forEach(el => {
  const height = el.offsetHeight; // Read
  el.style.height = height + 10 + 'px'; // Write
});

// ✅ Batch reads, then batch writes
const heights = elements.map(el => el.offsetHeight); // All reads
elements.forEach((el, i) => {
  el.style.height = heights[i] + 10 + 'px'; // All writes
});
javascript
// ❌ 导致多次重排
elements.forEach(el => {
  const height = el.offsetHeight; // 读取
  el.style.height = height + 10 + 'px'; // 写入
});

// ✅ 批量读取,再批量写入
const heights = elements.map(el => el.offsetHeight); // 所有读取操作
elements.forEach((el, i) => {
  el.style.height = heights[i] + 10 + 'px'; // 所有写入操作
});

Debounce expensive operations

防抖昂贵操作

javascript
function debounce(fn, delay) {
  let timeout;
  return (...args) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => fn(...args), delay);
  };
}

// Debounce scroll/resize handlers
window.addEventListener('scroll', debounce(handleScroll, 100));
javascript
function debounce(fn, delay) {
  let timeout;
  return (...args) => {
    clearTimeout(timeout);
    timeout = setTimeout(() => fn(...args), delay);
  };
}

// 防抖滚动/ resize事件处理器
window.addEventListener('scroll', debounce(handleScroll, 100));

Use requestAnimationFrame

使用requestAnimationFrame

javascript
// ❌ May cause jank
setInterval(animate, 16);

// ✅ Synced with display refresh
function animate() {
  // Animation logic
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
javascript
// ❌ 可能导致卡顿
setInterval(animate, 16);

// ✅ 与显示器刷新率同步
function animate() {
  // 动画逻辑
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);

Virtualize long lists

长列表虚拟化

javascript
// For lists > 100 items, render only visible items
// Use libraries like react-window, vue-virtual-scroller, or native CSS:
.virtual-list {
  content-visibility: auto;
  contain-intrinsic-size: 0 50px; /* Estimated item height */
}
javascript
// 列表项超过100个时,仅渲染可见项
// 使用react-window、vue-virtual-scroller等库,或原生CSS:
.virtual-list {
  content-visibility: auto;
  contain-intrinsic-size: 0 50px; /* 预估项高度 */
}

Third-party scripts

第三方脚本

Load strategies

加载策略

javascript
// ❌ Blocks main thread
<script src="https://analytics.example.com/script.js"></script>

// ✅ Async loading
<script async src="https://analytics.example.com/script.js"></script>

// ✅ Delay until interaction
<script>
document.addEventListener('DOMContentLoaded', () => {
  const observer = new IntersectionObserver((entries) => {
    if (entries[0].isIntersecting) {
      const script = document.createElement('script');
      script.src = 'https://widget.example.com/embed.js';
      document.body.appendChild(script);
      observer.disconnect();
    }
  });
  observer.observe(document.querySelector('#widget-container'));
});
</script>
javascript
// ❌ 阻塞主线程
<script src="https://analytics.example.com/script.js"></script>

// ✅ 异步加载
<script async src="https://analytics.example.com/script.js"></script>

// ✅ 延迟到交互时加载
<script>
document.addEventListener('DOMContentLoaded', () => {
  const observer = new IntersectionObserver((entries) => {
    if (entries[0].isIntersecting) {
      const script = document.createElement('script');
      script.src = 'https://widget.example.com/embed.js';
      document.body.appendChild(script);
      observer.disconnect();
    }
  });
  observer.observe(document.querySelector('#widget-container'));
});
</script>

Facade pattern

外观占位模式

html
<!-- Show static placeholder until interaction -->
<div class="youtube-facade" 
     data-video-id="abc123" 
     onclick="loadYouTube(this)">
  <img src="/thumbnails/abc123.jpg" alt="Video title">
  <button aria-label="Play video"></button>
</div>
html
<!-- 显示静态占位符,直到用户交互 -->
<div class="youtube-facade" 
     data-video-id="abc123" 
     onclick="loadYouTube(this)">
  <img src="/thumbnails/abc123.jpg" alt="视频标题">
  <button aria-label="播放视频"></button>
</div>

Measurement

性能度量

Key metrics

关键指标

MetricTargetTool
LCP< 2.5sLighthouse, CrUX
FCP< 1.8sLighthouse
Speed Index< 3.4sLighthouse
TBT< 200msLighthouse
TTI< 3.8sLighthouse
指标目标值工具
LCP< 2.5秒Lighthouse、CrUX
FCP< 1.8秒Lighthouse
速度指数< 3.4秒Lighthouse
TBT< 200毫秒Lighthouse
TTI< 3.8秒Lighthouse

Testing commands

测试命令

bash
undefined
bash
undefined

Lighthouse CLI

Lighthouse CLI

npx lighthouse https://example.com --output html --output-path report.html
npx lighthouse https://example.com --output html --output-path report.html

Web Vitals library

Web Vitals库

import {onLCP, onINP, onCLS} from 'web-vitals'; onLCP(console.log); onINP(console.log); onCLS(console.log);
undefined
import {onLCP, onINP, onCLS} from 'web-vitals'; onLCP(console.log); onINP(console.log); onCLS(console.log);
undefined

References

参考资料

For Core Web Vitals specific optimizations, see Core Web Vitals.
有关Core Web Vitals的具体优化方案,请查看 Core Web Vitals