返回博客
技术 2025年2月15日 5 分钟阅读 · 1061 字

前端性能优化全攻略:从加载到渲染的 12 个关键指标

从 LCP、FID、CLS 三大核心 Web 指标出发,系统性优化前端加载、解析、渲染各阶段性能。

#前端性能 #Web Vitals #优化 #CDN
本文由 AI 辅助生成,经人工审核发布

Core Web Vitals

Google 用三个核心指标衡量网页体验:

指标全称目标衡量
LCPLargest Contentful Paint< 2.5s最大内容元素渲染时间
INPInteraction to Next Paint< 200ms交互响应延迟
CLSCumulative 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>

widthheight 属性可以让浏览器在图片加载前预留空间,避免 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);
}

transformopacity 在 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;
格式压缩率兼容性
Brotli20% 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 });

总结

性能优化遵循「测量 → 分析 → 优化 → 验证」循环:

  1. 用 Lighthouse 和 WebPageTest 测量当前性能
  2. 找出最大的瓶颈(加载 vs 渲染 vs 传输)
  3. 针对性优化(图片/JS/字体/CSS)
  4. 重新测量验证效果

记住:过早优化是万恶之源。先确保功能正确,再用数据驱动优化决策。