前端性能优化实战
前端性能优化是提升用户体验的关键。本文将介绍实用的前端性能优化技术和最佳实践,帮助开发者构建更快、更流畅的 Web 应用。
性能指标
1. Core Web Vitals
- LCP (Largest Contentful Paint): 最大内容绘制时间
- FID (First Input Delay): 首次输入延迟
- CLS (Cumulative Layout Shift): 累积布局偏移
2. 传统指标
- FCP (First Contentful Paint): 首次内容绘制
- TTI (Time to Interactive): 可交互时间
- TBT (Total Blocking Time): 总阻塞时间
3. 测量工具
- Lighthouse: Google Chrome 开发者工具
- WebPageTest: 详细的性能分析
- Chrome DevTools: 实时性能监控
资源优化
1. 图片优化
<picture> <source srcset="image.webp" type="image/webp"> <img src="image.jpg" alt="Description"> </picture>
// 懒加载 <img src="image.jpg" loading="lazy" alt="Description">
// 响应式图片 <img src="image.jpg" srcset="image-320w.jpg 320w, image-640w.jpg 640w, image-1024w.jpg 1024w" sizes="(max-width: 320px) 280px, (max-width: 640px) 600px, 800px" alt="Description">
|
2. 字体优化
@font-face { font-family: 'CustomFont'; src: url('font.woff2') format('woff2'); font-display: swap; }
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
|
3. 代码分割
const LazyComponent = React.lazy(() => import('./LazyComponent'));
const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue') );
|
网络优化
1. 缓存策略
const CACHE_NAME = 'my-app-cache-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 => response || fetch(event.request)) ); });
|
2. HTTP/2 优化
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js') .then(registration => { registration.postMessage({ type: 'SKIP_WAITING' }); }); }
|
3. CDN 加速
<link rel="stylesheet" href="https://cdn.example.com/styles/main.css"> <script src="https://cdn.example.com/scripts/main.js"></script>
|
渲染优化
1. 避免强制同步布局
function updateLayout() { const width = element.offsetWidth; element.style.height = width * 2 + 'px'; const height = element.offsetHeight; console.log(height); }
function updateLayout() { requestAnimationFrame(() => { const width = element.offsetWidth; element.style.height = width * 2 + 'px'; const height = element.offsetHeight; console.log(height); }); }
|
2. 使用 requestAnimationFrame
function animate() { element.style.transform = `translateX(${position}px)`; requestAnimationFrame(animate); }
requestAnimationFrame(animate);
|
3. 虚拟滚动
class VirtualScroll { constructor(container, items, renderItem) { this.container = container; this.items = items; this.renderItem = renderItem; this.visibleItems = []; this.itemHeight = 50; this.scrollTop = 0; this.init(); } init() { this.container.addEventListener('scroll', () => { this.scrollTop = this.container.scrollTop; this.render(); }); this.render(); } render() { const startIndex = Math.floor(this.scrollTop / this.itemHeight); const endIndex = Math.min( startIndex + Math.ceil(this.container.offsetHeight / this.itemHeight), this.items.length ); this.visibleItems = this.items.slice(startIndex, endIndex); this.container.innerHTML = this.visibleItems .map((item, index) => this.renderItem(item, startIndex + index)) .join(''); } }
|
JavaScript 优化
1. 防抖和节流
function debounce(func, delay) { let timeoutId; return function(...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(this, args), delay); }; }
function throttle(func, limit) { let inThrottle; return function(...args) { if (!inThrottle) { func.apply(this, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; }
window.addEventListener('resize', debounce(handleResize, 250));
|
2. 事件委托
document.querySelectorAll('.item').forEach(item => { item.addEventListener('click', handleClick); });
document.querySelector('.container').addEventListener('click', (e) => { if (e.target.classList.contains('item')) { handleClick(e); } });
|
3. 内存管理
function setupComponent() { const element = document.getElementById('my-element'); function handleClick() { } element.addEventListener('click', handleClick); return () => { element.removeEventListener('click', handleClick); }; }
const cleanup = setupComponent();
cleanup();
|
CSS 优化
1. 选择器优化
.container .item .content .title { color: blue; }
.content-title { color: blue; }
|
2. 动画优化
.animated-element { will-change: transform, opacity; transition: transform 0.3s ease; }
.animated-element:hover { transform: scale(1.1); opacity: 0.9; }
|
3. 减少重绘和回流
.hidden { visibility: hidden; }
.movable { transform: translateX(100px); }
|
工具和框架
1. 构建工具优化
module.exports = { optimization: { splitChunks: { chunks: 'all', minSize: 20000, maxSize: 244000, minChunks: 1, maxAsyncRequests: 5, maxInitialRequests: 3, automaticNameDelimiter: '~', cacheGroups: { vendors: { test: /[\\/]node_modules[\\/]/, priority: -10 }, default: { minChunks: 2, priority: -20, reuseExistingChunk: true } } } } };
|
2. 框架优化
import React, { memo, useCallback, useMemo } from 'react';
const OptimizedComponent = memo(({ data, onAction }) => { const processedData = useMemo(() => { return data.map(item => ({ ...item, processed: true })); }, [data]); const handleClick = useCallback(() => { onAction(); }, [onAction]); return ( <div> {processedData.map(item => ( <div key={item.id} onClick={handleClick}> {item.name} </div> ))} </div> ); });
|
3. 性能监控
const observer = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { console.log(`${entry.name}: ${entry.duration}ms`); } });
observer.observe({ entryTypes: ['measure'] });
performance.mark('start');
performance.mark('end'); performance.measure('my-operation', 'start', 'end');
|
实战案例
1. 首屏优化
<link rel="preload" href="critical.css" as="style"> <link rel="preload" href="critical.js" as="script">
// 懒加载非关键资源 const loadNonCriticalResources = () => { import('./non-critical.js').then(module => { module.init(); }); };
// 页面加载完成后执行 window.addEventListener('load', loadNonCriticalResources);
|
2. 图片懒加载实现
class LazyLoader { constructor() { this.images = document.querySelectorAll('img[data-src]'); this.observer = new IntersectionObserver( this.handleIntersection.bind(this), { rootMargin: '50px' } ); this.init(); } init() { this.images.forEach(img => { this.observer.observe(img); }); } handleIntersection(entries) { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; img.src = img.dataset.src; img.classList.remove('lazy'); this.observer.unobserve(img); } }); } }
new LazyLoader();
|
3. Service Worker 缓存策略
const CACHE_NAME = 'my-app-cache-v2'; const ASSETS_URLS = [ '/', '/index.html', '/styles/main.css', '/scripts/main.js' ];
self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => cache.addAll(ASSETS_URLS)) ); });
self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.filter(name => name !== CACHE_NAME) .map(name => caches.delete(name)) ); }) ); });
self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request) .then(response => response || fetch(event.request)) ); });
|
前端性能优化是一个持续的过程,需要结合项目实际情况进行优化。通过合理的优化策略,可以显著提升用户体验和应用性能。