返回博客
技术 2025年2月15日 5 分钟阅读 · 1061 字
前端性能优化全攻略:从加载到渲染的 12 个关键指标
从 LCP、FID、CLS 三大核心 Web 指标出发,系统性优化前端加载、解析、渲染各阶段性能。
#前端性能
#Web Vitals
#优化
#CDN
本文由 AI 辅助生成,经人工审核发布
Core Web Vitals
Google 用三个核心指标衡量网页体验:
| 指标 | 全称 | 目标 | 衡量 |
|---|---|---|---|
| LCP | Largest Contentful Paint | < 2.5s | 最大内容元素渲染时间 |
| INP | Interaction to Next Paint | < 200ms | 交互响应延迟 |
| CLS | Cumulative Layout Shift | < 0.1 | 累积布局偏移 |
加载阶段优化
1. 关键渲染路径
浏览器渲染前的步骤:
HTML 下载 → HTML 解析 → CSS 下载 → CSS 解析 → DOM + CSSOM → 渲染树 → 布局 → 绘制
优化目标:减少阻塞渲染的资源。
2. CSS 优化
<!-- ❌ 阻塞渲染 -->
<link rel="stylesheet" href="/css/all.css">
<!-- ✅ 关键 CSS 内联,非关键 CSS 异步加载 -->
<style>
/* 内联首屏关键 CSS */
body { margin: 0; font-family: system-ui; }
.hero { min-height: 100vh; }
</style>
<link rel="preload" href="/css/rest.css" as="style" onload="this.rel='stylesheet'">
3. JavaScript 优化
<!-- defer:HTML 解析完成后执行,不阻塞 -->
<script src="/js/app.js" defer></script>
<!-- async:下载不阻塞,下载完立即执行 -->
<script src="/js/analytics.js" async></script>
<!-- 动态导入:按需加载 -->
<script>
const module = await import('./heavy-module.js');
</script>
| 属性 | 下载 | 执行 | 顺序 |
|---|---|---|---|
| 无 | 阻塞 | 阻塞 | 按顺序 |
| defer | 不阻塞 | DOM 解析后 | 按顺序 |
| async | 不阻塞 | 下载完即执行 | 无序 |
4. 图片优化
<!-- 响应式图片:根据屏幕尺寸加载不同图 -->
<img
src="image-800.webp"
srcset="image-400.webp 400w, image-800.webp 800w, image-1200.webp 1200w"
sizes="(max-width: 600px) 400px, (max-width: 1200px) 800px, 1200px"
alt="描述"
loading="lazy"
decoding="async"
width="800" height="600"
/>
<!-- 格式优先级:AVIF > WebP > JPEG -->
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="描述">
</picture>
width 和 height 属性可以让浏览器在图片加载前预留空间,避免 CLS。
5. 字体优化
/* 字体加载策略 */
@font-face {
font-family: 'CustomFont';
src: url('/fonts/custom.woff2') format('woff2');
font-display: swap; /* 先用系统字体,字体加载后切换 */
}
<!-- 预连接字体 CDN -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preload" href="/fonts/custom.woff2" as="font" type="font/woff2" crossorigin>
渲染阶段优化
6. 减少重排(Reflow)
// ❌ 触发多次重排
element.style.width = '100px';
element.style.height = '200px';
element.style.margin = '10px';
// ✅ 使用 class 一次性修改
element.classList.add('resized');
.resized {
width: 100px;
height: 200px;
margin: 10px;
}
7. 使用 transform 和 opacity 做动画
/* ❌ 触发布局 */
.animate {
transition: left 0.3s, top 0.3s;
left: 100px;
top: 200px;
}
/* ✅ 仅触发合成,不影响布局 */
.animate {
transition: transform 0.3s;
transform: translate(100px, 200px);
}
transform 和 opacity 在 GPU 合成层处理,不触发布局重计算。
8. 虚拟列表
渲染 10000 条数据时,只渲染可视区域:
// 使用 IntersectionObserver 实现简易虚拟列表
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// 加载该位置的数据
renderItem(entry.target);
}
});
});
document.querySelectorAll('.lazy-item').forEach(el => observer.observe(el));
传输优化
9. 资源压缩
# Nginx 配置:Brotli > Gzip
brotli on;
brotli_types text/css application/javascript application/json image/svg+xml;
brotli_comp_level 6;
| 格式 | 压缩率 | 兼容性 |
|---|---|---|
| Brotli | 20% better than Gzip | 现代浏览器 |
| Gzip | 基准 | 所有浏览器 |
10. CDN 边缘缓存
# 静态资源缓存策略
location ~* \.(js|css|png|jpg|webp|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# HTML 文件短缓存
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
immutable 告诉浏览器:文件永远不会变化,不需要发送条件请求验证。
11. HTTP/2 Server Push → Resource Hints
HTTP/2 Push 已被废弃,改用 Resource Hints:
<!-- 预连接:提前建立 TCP + TLS 连接 -->
<link rel="preconnect" href="https://cdn.example.com">
<!-- 预加载:提前下载高优先级资源 -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<!-- 预获取:空闲时下载下一页可能需要的资源 -->
<link rel="prefetch" href="/next-page-data.json">
<!-- 预渲染:在后台渲染整个页面 -->
<link rel="prerender" href="/likely-next-page">
12. 代码分割
// 路由级别分割
const routes = [
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue'),
},
{
path: '/settings',
component: () => import('./views/Settings.vue'),
},
];
// 组件级别分割
const HeavyChart = defineAsyncComponent(() =>
import('./components/HeavyChart.vue')
);
Vite 会自动将动态 import() 拆分为独立 chunk。
性能监测
// 使用 Performance API 监测 LCP
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
console.log('LCP:', lastEntry.startTime, 'ms');
}).observe({ type: 'largest-contentful-paint', buffered: true });
// 监测 CLS
let clsValue = 0;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
clsValue += entry.value;
}
console.log('CLS:', clsValue);
}).observe({ type: 'layout-shift', buffered: true });
总结
性能优化遵循「测量 → 分析 → 优化 → 验证」循环:
- 用 Lighthouse 和 WebPageTest 测量当前性能
- 找出最大的瓶颈(加载 vs 渲染 vs 传输)
- 针对性优化(图片/JS/字体/CSS)
- 重新测量验证效果
记住:过早优化是万恶之源。先确保功能正确,再用数据驱动优化决策。