前端性能优化实战:从加载到渲染全面提速
⚡ 性能优化核心目标
- 首屏加载时间 < 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
| <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
| <style> .header { ... } .hero { ... } </style>
<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
| 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
| const routes = [ { path: '/dashboard', component: () => import( './views/Dashboard.vue') } ];
export default { components: { HeavyComponent: () => import('./HeavyComponent.vue') } };
|
🗜️ 资源压缩
Gzip/Brotli压缩
1 2 3 4 5 6 7 8
| gzip on; gzip_types text/plain text/css application/json application/javascript text/xml; gzip_min_length 1000;
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
| 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
| 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
| 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
| .box { transition: transform 0.3s ease; } .box:hover { transform: translateX(100px); }
.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
| 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} />); }
|
📈 性能监控
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: timing.domainLookupEnd - timing.domainLookupStart, tcp: timing.connectEnd - timing.connectStart, ttfb: timing.responseStart - timing.requestStart, domParse: timing.domContentLoadedEventEnd - timing.domLoading, loadTime: timing.loadEventEnd - timing.navigationStart, }; console.log(metrics); });
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);
|
✅ 性能优化检查清单
加载优化
缓存优化
渲染优化
代码优化
性能优化是一个持续的过程,让我们一起追求极致体验! 🚀