前端性能优化实战:从加载到渲染全面提速

性能优化核心目标

  • 首屏加载时间 < 2秒
  • 可交互时间 (TTI) < 3秒
  • 页面流畅度 60fps

📊 性能指标解读

Core Web Vitals

指标说明优秀标准
LCP最大内容绘制< 2.5s
FID首次输入延迟< 100ms
CLS累计布局偏移< 0.1

🚀 资源加载优化

1. 图片优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- 使用WebP格式 -->
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" alt="描述">
</picture>

<!-- 响应式图片 -->
<img
srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 480px, (max-width: 1000px) 800px, 1200px"
src="large.jpg"
alt="响应式图片"
>

<!-- 懒加载 -->
<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" alt="懒加载图片">

2. JavaScript优化

1
2
3
4
5
6
7
8
9
<!-- 异步加载非关键脚本 -->
<script src="analytics.js" async></script>

<!-- 延迟加载 -->
<script src="non-critical.js" defer></script>

<!-- 预加载关键资源 -->
<link rel="preload" href="critical.js" as="script">
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>

3. CSS优化

1
2
3
4
5
6
7
8
9
10
<!-- 关键CSS内联 -->
<style>
/* 首屏关键样式 */
.header { ... }
.hero { ... }
</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>

📦 代码分割与懒加载

Webpack代码分割

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// webpack.config.js
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
common: {
minChunks: 2,
name: 'common',
chunks: 'all',
},
},
},
},
};

React懒加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import React, { Suspense, lazy } from 'react';

// 路由懒加载
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));

function App() {
return (
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Suspense>
);
}

Vue懒加载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Vue Router
const routes = [
{
path: '/dashboard',
component: () => import(/* webpackChunkName: "dashboard" */ './views/Dashboard.vue')
}
];

// 组件懒加载
export default {
components: {
HeavyComponent: () => import('./HeavyComponent.vue')
}
};

🗜️ 资源压缩

Gzip/Brotli压缩

1
2
3
4
5
6
7
8
# Nginx配置
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 1000;

# Brotli压缩(更高压缩率)
brotli on;
brotli_types text/plain text/css application/json application/javascript;

图片压缩配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// webpack配置
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');

module.exports = {
plugins: [
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminMinify,
options: {
plugins: [
['mozjpeg', { quality: 80 }],
['pngquant', { quality: [0.6, 0.8] }],
['svgo', { plugins: [{ removeViewBox: false }] }],
],
},
},
}),
],
};

💾 缓存策略

HTTP缓存

1
2
3
4
5
6
7
8
9
10
# Nginx缓存配置
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}

location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}

Service Worker缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// sw.js
const CACHE_NAME = 'v1';
const urlsToCache = [
'/',
'/styles/main.css',
'/scripts/main.js',
];

// 安装
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});

// 请求拦截
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
if (response) {
return response; // 返回缓存
}
return fetch(event.request); // 请求网络
})
);
});

🎨 渲染性能优化

避免强制同步布局

1
2
3
4
5
6
7
8
9
10
11
// ❌ 强制同步布局
elements.forEach(el => {
const width = el.offsetWidth; // 读取
el.style.width = width + 10 + 'px'; // 写入 -> 触发重排
});

// ✅ 批量读取后批量写入
const widths = elements.map(el => el.offsetWidth); // 批量读取
elements.forEach((el, i) => {
el.style.width = widths[i] + 10 + 'px'; // 批量写入
});

使用CSS动画代替JS

1
2
3
4
5
6
7
8
9
10
11
12
/* ✅ 使用CSS transform动画 */
.box {
transition: transform 0.3s ease;
}
.box:hover {
transform: translateX(100px);
}

/* ✅ 使用will-change提示浏览器 */
.animated-element {
will-change: transform, opacity;
}

虚拟列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// React虚拟列表示例
import { FixedSizeList } from 'react-window';

function VirtualList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
{items[index].name}
</div>
);

return (
<FixedSizeList
height={400}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</FixedSizeList>
);
}

📱 移动端优化

触摸响应优化

1
2
3
4
5
6
7
8
9
10
/* 禁用点击延迟 */
html {
touch-action: manipulation;
}

/* 优化滚动 */
.scroll-container {
-webkit-overflow-scrolling: touch;
overflow-y: scroll;
}

骨架屏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function SkeletonCard() {
return (
<div className="skeleton-card">
<div className="skeleton-image animate-pulse"></div>
<div className="skeleton-title animate-pulse"></div>
<div className="skeleton-text animate-pulse"></div>
</div>
);
}

function ProductList({ loading, products }) {
if (loading) {
return Array(6).fill(0).map((_, i) => <SkeletonCard key={i} />);
}
return products.map(p => <ProductCard key={p.id} product={p} />);
}

📈 性能监控

Performance API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 页面加载性能
window.addEventListener('load', () => {
const timing = performance.timing;
const metrics = {
// DNS查询时间
dns: timing.domainLookupEnd - timing.domainLookupStart,
// TCP连接时间
tcp: timing.connectEnd - timing.connectStart,
// 首字节时间
ttfb: timing.responseStart - timing.requestStart,
// DOM解析时间
domParse: timing.domContentLoadedEventEnd - timing.domLoading,
// 页面完全加载时间
loadTime: timing.loadEventEnd - timing.navigationStart,
};
console.log(metrics);
// 上报到监控系统
});

// 监控LCP
new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
const lastEntry = entries[entries.length - 1];
console.log('LCP:', lastEntry.startTime);
}).observe({ entryTypes: ['largest-contentful-paint'] });

Web Vitals监控

1
2
3
4
5
6
7
8
9
10
import { getCLS, getFID, getLCP } from 'web-vitals';

function sendToAnalytics(metric) {
const body = JSON.stringify(metric);
navigator.sendBeacon('/analytics', body);
}

getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getLCP(sendToAnalytics);

✅ 性能优化检查清单

加载优化

  • 开启Gzip/Brotli压缩
  • 图片使用WebP格式
  • 实现图片懒加载
  • 代码分割和按需加载
  • 使用CDN加速

缓存优化

  • 配置合理的HTTP缓存
  • 使用Service Worker
  • 静态资源添加hash

渲染优化

  • 减少DOM操作
  • 使用CSS动画
  • 长列表虚拟化
  • 避免强制同步布局

代码优化

  • Tree Shaking
  • 代码压缩混淆
  • 移除未使用代码

性能优化是一个持续的过程,让我们一起追求极致体验! 🚀