🚀 快速安装

复制以下命令并运行,立即安装此 Skill:

npx skills add https://github.com/addyosmani/web-quality-skills --skill performance

💡 提示:需要 Node.js 和 NPM

性能优化

基于 Lighthouse 性能审核的深度性能优化。重点关注加载速度、运行时效率和资源优化。

工作原理

  1. 识别代码和资产中的性能瓶颈
  2. 根据对核心 Web 指标的影响确定优先级
  3. 提供带有代码示例的特定优化方案
  4. 通过优化前后的指标衡量改进效果

性能预算

资源 预算 理由
总页面大小 < 1.5 MB 3G 网络约 4 秒加载完成
JavaScript(压缩后) < 300 KB 解析和执行时间
CSS(压缩后) < 100 KB 阻塞渲染
图片(首屏以上) < 500 KB 影响最大内容绘制
字体 < 100 KB 防止不可见文本闪烁/样式化文本闪烁
第三方脚本 < 200 KB 不可控的延迟

关键渲染路径

服务器响应

  • 首字节时间 < 800ms。 首字节时间应尽可能快。使用 CDN、缓存和高效的后端。
  • 启用压缩。 对文本资产使用 Gzip 或 Brotli。首选 Brotli(体积小 15-20%)。
  • HTTP/2 或 HTTP/3。 多路复用减少连接开销。
  • 边缘缓存。 尽可能在 CDN 边缘缓存 HTML。

资源加载

预连接到必需的源:

<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>

预加载关键资源:

<!-- 最大内容绘制图片 -->
<link rel="preload" href="/hero.webp" as="image" fetchpriority="high">

<!-- 关键字体 -->
<link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossorigin>

延迟非关键 CSS:

<!-- 内联关键 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 优化

延迟非必要脚本:

<!-- 阻塞解析器 (避免) -->
<script src="/critical.js"></script>

<!-- 延迟 (首选) -->
<script defer src="/app.js"></script>

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

<!-- 模块 (默认延迟) -->
<script type="module" src="/app.mjs"></script>

代码分割模式:

// 基于路由的分割
const Dashboard = lazy(() => import('./Dashboard'));

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

// 基于功能的分割
if (user.isPremium) {
  const PremiumFeatures = await import('./PremiumFeatures');
}

摇树优化最佳实践:

// ❌ 导入整个库
import _ from 'lodash';
_.debounce(fn, 300);

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

图片优化

格式选择

格式 用例 浏览器支持
AVIF 照片,最佳压缩率 92%+
WebP 照片,良好的后备方案 97%+
PNG 带透明度的图形 通用
SVG 图标、标志、插图 通用

响应式图片

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

最大内容绘制图片优先级

<!-- 首屏最大内容绘制图片:立即加载,高优先级 -->
<img 
  src="hero.webp" 
  fetchpriority="high"
  loading="eager"
  decoding="sync"
  alt="主视觉">

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

字体优化

加载策略

/* 系统字体堆栈作为后备 */
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; /* 子集化到拉丁字符集 */
}

预加载关键字体

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

可变字体

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

缓存策略

缓存控制头

# HTML(短缓存或不缓存)
Cache-Control: no-cache, must-revalidate

# 带哈希的静态资源(不可变)
Cache-Control: public, max-age=31536000, immutable

# 不带哈希的静态资源
Cache-Control: public, max-age=86400, stale-while-revalidate=604800

# API 响应
Cache-Control: private, max-age=0, must-revalidate

Service Worker 缓存

// 静态资源优先从缓存读取
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;
        });
      })
    );
  }
});

运行时性能

避免布局抖动

// ❌ 强制多次重排
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'; // 所有写操作
});

防抖昂贵操作

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

// 防抖滚动/调整大小处理程序
window.addEventListener('scroll', debounce(handleScroll, 100));

使用 requestAnimationFrame

// ❌ 可能导致卡顿
setInterval(animate, 16);

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

虚拟化长列表

// 对于超过 100 个项目的列表,仅渲染可见项
// 使用像 react-window, vue-virtual-scroller 这样的库,或原生 CSS:
.virtual-list {
  content-visibility: auto;
  contain-intrinsic-size: 0 50px; /* 估计的项目高度 */
}

第三方脚本

加载策略

// ❌ 阻塞主线程
<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>

外观模式

<!-- 交互前显示静态占位符 -->
<div class="youtube-facade" 
     data-video-id="abc123" 
     onclick="loadYouTube(this)">
  <img src="/thumbnails/abc123.jpg" alt="视频标题">
  <button aria-label="播放视频"></button>
</div>

衡量指标

关键指标

指标 目标值 工具
最大内容绘制 < 2.5s Lighthouse, CrUX
首次内容绘制 < 1.8s Lighthouse
速度指数 < 3.4s Lighthouse
总阻塞时间 < 200ms Lighthouse
可交互时间 < 3.8s Lighthouse

测试命令

# Lighthouse 命令行
npx lighthouse https://example.com --output html --output-path report.html

# Web Vitals 库
import {onLCP, onINP, onCLS} from 'web-vitals';
onLCP(console.log);
onINP(console.log);
onCLS(console.log);

参考

有关核心 Web 指标的特定优化,请参阅 核心 Web 指标

📄 原始文档

完整文档(英文):

https://skills.sh/addyosmani/web-quality-skills/performance

💡 提示:点击上方链接查看 skills.sh 原始英文文档,方便对照翻译。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。