前端性能优化策略
性能为什么重要
研究表明,页面加载时间每增加 1 秒,转化率下降 7%,用户满意度下降 16%。在移动端,53% 的用户会在 3 秒内关闭加载过慢的页面。性能优化直接影响用户体验和业务指标。
核心性能指标
Web Vitals
- LCP(Largest Contentful Paint):最大内容绘制,≤2.5s
- FID(First Input Delay):首次输入延迟,≤100ms
- CLS(Cumulative Layout Shift):累计布局偏移,≤0.1
其他关键指标
- TTFB(Time to First Byte):首字节时间,≤800ms
- FCP(First Contentful Paint):首次内容绘制,≤1.8s
- TBT(Total Blocking Time):总阻塞时间,≤200ms
加载优化
资源压缩
// Webpack 可使用 TerserPlugin 压缩 JS
// CSS 使用 CssMinimizerPlugin
// 图片使用 imagemin
代码分割
// React 动态导入
const Dashboard = React.lazy(() => import('./Dashboard'));
// Vue 动态导入
const Dashboard = () => import('./Dashboard.vue');
图片优化
<!-- WebP 格式比 JPEG 小 25-35% -->
<picture>
<source type="image/webp" srcset="image.webp">
<img src="image.jpg" alt="描述">
</picture>
<!-- 懒加载 -->
<img loading="lazy" src="image.jpg" alt="描述">
<!-- 使用 srcset 提供多尺寸 -->
<img
src="small.jpg"
srcset="medium.jpg 768w, large.jpg 1024w"
sizes="(max-width: 767px) 100vw, 50vw"
alt="描述"
>
字体优化
/* 使用 font-display 避免字体闪烁 */
@font-face {
font-family: 'MyFont';
src: url('/fonts/myfont.woff2') format('woff2');
font-display: swap; /* 先用后备字体,加载完成后替换 */
}
/* 子集字体只包含需要的字符 */
/* 使用 WOFF2 格式 */
渲染优化
避免布局抖动
/* 为动态内容预留空间 */
.card-image {
width: 100%;
aspect-ratio: 16 / 9; /* 固定宽高比 */
/* 或使用 width + padding-top 的传统方案 */
}
使用 will-change
.animated-element {
will-change: transform, opacity;
/* 提前告知浏览器要变化的属性 */
}
减少重排重绘
// 批量修改 DOM,使用 DocumentFragment
const fragment = document.createDocumentFragment();
items.forEach(item => {
const li = document.createElement('li');
li.textContent = item;
fragment.appendChild(li);
});
list.appendChild(fragment);
// 读写分离:避免强制同步布局
// 错误:先读后写交替进行
// 正确:先批量读取,再批量写入
虚拟列表
// 渲染可视区域内的元素,而非全部
// 适用于长列表(数千条以上)
// 库:react-window、vue-virtual-scroller
网络优化
CDN 加速
<!-- 使用 CDN 托管静态资源 -->
<script src="https://cdn.example.com/js/app.[hash].js"></script>
预加载关键资源
<!-- 预加载关键 CSS/字体 -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="/fonts/roboto.woff2" as="font" crossorigin>
<!-- 预连接第三方源 -->
<link rel="preconnect" href="https://api.example.com">
<!-- 预获取下一页 -->
<link rel="prefetch" href="/next-page">
HTTP 缓存
# Nginx 配置示例
location /static/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
运行时优化
防抖与节流
// 防抖:连续操作后执行一次
function debounce(fn, delay) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}
// 节流:固定频率执行
function throttle(fn, interval) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= interval) {
last = now;
fn(...args);
}
};
}
Web Worker
// main.js
const worker = new Worker('worker.js');
worker.postMessage({ data: largeArray });
worker.onmessage = (e) => console.log(e.data);
// worker.js
self.onmessage = (e) => {
const result = expensiveCalculation(e.data);
self.postMessage(result);
};
性能监测
// Performance API
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log(`${entry.name}: ${entry.startTime}ms`);
}
});
observer.observe({ entryTypes: ['largest-contentful-paint', 'layout-shift'] });
// Web Vitals 库
import { getLCP, getFID, getCLS } from 'web-vitals';
getLCP(console.log);
getFID(console.log);
getCLS(console.log);
总结
性能优化是一个持续的过程,建议先测量再优化。使用 Lighthouse 或 WebPageTest 找出瓶颈,制定针对性的优化方案。记住:20% 的优化手段能解决 80% 的性能问题,优先做投入产出比高的优化。