performance
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePerformance optimization
网页性能优化
Deep performance optimization based on Lighthouse performance audits. Focuses on loading speed, runtime efficiency, and resource optimization.
基于Lighthouse性能审计的深度性能优化,重点关注加载速度、运行时效率和资源优化。
How it works
工作原理
- Identify performance bottlenecks in code and assets
- Prioritize by impact on Core Web Vitals
- Provide specific optimizations with code examples
- Measure improvement with before/after metrics
- 识别代码和资源中的性能瓶颈
- 根据对Core Web Vitals的影响优先级排序
- 提供带有代码示例的具体优化方案
- 通过优化前后的指标衡量改进效果
Performance budget
性能预算
| Resource | Budget | Rationale |
|---|---|---|
| Total page weight | < 1.5 MB | 3G loads in ~4s |
| JavaScript (compressed) | < 300 KB | Parsing + execution time |
| CSS (compressed) | < 100 KB | Render blocking |
| Images (above-fold) | < 500 KB | LCP impact |
| Fonts | < 100 KB | FOIT/FOUT prevention |
| Third-party | < 200 KB | Uncontrolled latency |
| 资源 | 预算 | 依据 |
|---|---|---|
| 页面总大小 | < 1.5 MB | 3G网络下约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
格式选择
| Format | Use case | Browser support |
|---|---|---|
| AVIF | Photos, best compression | 92%+ |
| WebP | Photos, good fallback | 97%+ |
| PNG | Graphics with transparency | Universal |
| SVG | Icons, logos, illustrations | Universal |
| 格式 | 使用场景 | 浏览器支持 |
|---|---|---|
| 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响应头
undefinedundefinedHTML (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
undefinedCache-Control: private, max-age=0, must-revalidate
undefinedService 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
关键指标
| Metric | Target | Tool |
|---|---|---|
| LCP | < 2.5s | Lighthouse, CrUX |
| FCP | < 1.8s | Lighthouse |
| Speed Index | < 3.4s | Lighthouse |
| TBT | < 200ms | Lighthouse |
| TTI | < 3.8s | Lighthouse |
| 指标 | 目标值 | 工具 |
|---|---|---|
| LCP | < 2.5秒 | Lighthouse、CrUX |
| FCP | < 1.8秒 | Lighthouse |
| 速度指数 | < 3.4秒 | Lighthouse |
| TBT | < 200毫秒 | Lighthouse |
| TTI | < 3.8秒 | Lighthouse |
Testing commands
测试命令
bash
undefinedbash
undefinedLighthouse 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);
undefinedimport {onLCP, onINP, onCLS} from 'web-vitals';
onLCP(console.log);
onINP(console.log);
onCLS(console.log);
undefinedReferences
参考资料
For Core Web Vitals specific optimizations, see Core Web Vitals.
有关Core Web Vitals的具体优化方案,请查看 Core Web Vitals。