关于 performance
此技能根据 Lighthouse 审计提供 Web 性能优化指导,帮助开发者定位瓶颈、按 Core Web Vitals 确定优先级,并通过前后指标评估代码和资源调整。
主要功能
- 01定位代码和资源瓶颈
- 02按 Core Web Vitals 排序
- 03优化加载与渲染
- 04提升运行时效率
- 05测量优化前后效果
适用场景
- 01诊断页面加载缓慢
- 02降低 JavaScript 和资源开销
- 03改善渲染与交互性能
- 04用 Lighthouse 指标评估改动
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ davila7/ claude-code-templates --skill performance安装后在 Claude Code 中输入/performance确认已加载。
详细介绍
性能优化
基于 Lighthouse 性能审计进行深入的性能优化,重点关注加载速度、运行时效率和资源优化。
工作原理
- 识别代码和资源中的性能瓶颈
- 根据对 Core Web Vitals 的影响确定优先级
- 提供具体的优化方案和代码示例
- 使用优化前后的指标衡量改进效果
性能预算
| 资源 | 预算 | 原因 |
|---|---|---|
| 页面总大小 | < 1.5 MB | 3G 网络下约 4 秒加载完成 |
| JavaScript(压缩后) | < 300 KB | 解析和执行耗时 |
| CSS(压缩后) | < 100 KB | 阻塞渲染 |
| 图片(首屏) | < 500 KB | 影响 LCP |
| 字体 | < 100 KB | 防止 FOIT/FOUT |
| 第三方资源 | < 200 KB | 延迟不可控 |
关键渲染路径
服务器响应
- TTFB < 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:
<style>/* Above-fold styles */</style>
<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>
代码拆分模式:
// 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 最佳实践:
// ❌ Imports entire library
import _ from 'lodash';
_.debounce(fn, 300);
// ✅ Imports only what's needed
import debounce from 'lodash/debounce';
debounce(fn, 300);
图片优化
格式选择
| 格式 | 使用场景 | 浏览器支持率 |
|---|---|---|
| AVIF | 照片,压缩效果最佳 | 92%+ |
| WebP | 照片,良好的后备格式 | 97%+ |
| PNG | 带透明背景的图形 | 通用 |
| SVG | 图标、徽标、插画 | 通用 |
响应式图片
<picture>
<source
type="image/avif"
srcset="hero-400.avif 400w,
hero-800.avif 800w,
hero-1200.avif 1200w"
sizes="(max-width: 600px) 100vw, 50vw">
<source
type="image/webp"
srcset="hero-400.webp 400w,
hero-800.webp 800w,
hero-1200.webp 1200w"
sizes="(max-width: 600px) 100vw, 50vw">
<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>
LCP 图片优先级
<img
src="hero.webp"
fetchpriority="high"
loading="eager"
decoding="sync"
alt="Hero">
<img
src="product.webp"
loading="lazy"
decoding="async"
alt="Product">
字体优化
加载策略
/* 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 */
}
预加载关键字体
<link rel="preload" href="/fonts/heading.woff2" as="font" type="font/woff2" crossorigin>
可变字体
/* 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;
}
缓存策略
Cache-Control 标头
# HTML (short or no cache)
Cache-Control: no-cache, must-revalidate
# Static assets with hash (immutable)
Cache-Control: public, max-age=31536000, immutable
# Static assets without hash
Cache-Control: public, max-age=86400, stale-while-revalidate=604800
# API responses
Cache-Control: private, max-age=0, must-revalidate
Service Worker 缓存
// 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;
});
})
);
}
});
运行时性能
避免布局抖动
// ❌ 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
});
对耗时操作进行防抖处理
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));
使用 requestAnimationFrame
// ❌ May cause jank
setInterval(animate, 16);
// ✅ Synced with display refresh
function animate() {
// Animation logic
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
对长列表进行虚拟化
// 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 */
}
第三方脚本
加载策略
// ❌ 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>
门面模式
<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>
测量
关键指标
| 指标 | 目标 | 工具 |
|---|---|---|
| LCP | < 2.5s | Lighthouse、CrUX |
| FCP | < 1.8s | Lighthouse |
| Speed Index | < 3.4s | Lighthouse |
| TBT | < 200ms | Lighthouse |
| TTI | < 3.8s | Lighthouse |
测试命令
# Lighthouse CLI
npx lighthouse https://example.com --output html --output-path report.html
# Web Vitals library
import {onLCP, onINP, onCLS} from 'web-vitals';
onLCP(console.log);
onINP(console.log);
onCLS(console.log);
参考资料
有关 Core Web Vitals 的特定优化,请参阅 Core Web Vitals。
评论
支持 Markdown