前端性能优化
目录
加载优化
资源压缩
javascript
// webpack.config.js
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = {
plugins: [
new CompressionPlugin({
test: /\.(js|css|html|svg)$/,
algorithm: 'gzip',
threshold: 10240,
minRatio: 0.8
})
]
};
// 图片压缩
const imagemin = require('imagemin');
const imageminMozjpeg = require('imagemin-mozjpeg');
const imageminPngquant = require('imagemin-pngquant');
(async () => {
await imagemin(['images/*'], {
destination: 'compressed_images',
plugins: [
imageminMozjpeg({ quality: 75 }),
imageminPngquant({ quality: [0.6, 0.8] })
]
});
})();说明:
- 压缩 JavaScript、CSS、HTML 文件
- 压缩图片资源
- 使用 Gzip/Brotli 压缩
- 设置合适的压缩率
代码分割
javascript
// 路由懒加载
const About = lazy(() => import('./About'));
const App = () => {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
);
};
// 组件懒加载
const HeavyComponent = lazy(() => import('./HeavyComponent'));
const Parent = () => {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
};说明:
- 按路由分割代码
- 按组件分割代码
- 使用动态导入
- 添加加载状态
懒加载
javascript
// 图片懒加载
const LazyImage = ({ src, alt }) => {
const [isLoaded, setIsLoaded] = useState(false);
return (
<img
src={src}
alt={alt}
loading="lazy"
onLoad={() => setIsLoaded(true)}
style={{ opacity: isLoaded ? 1 : 0 }}
/>
);
};
// 列表懒加载
const InfiniteList = () => {
const [items, setItems] = useState([]);
const [page, setPage] = useState(1);
const loadMore = useCallback(() => {
fetchItems(page).then(newItems => {
setItems(prev => [...prev, ...newItems]);
setPage(prev => prev + 1);
});
}, [page]);
return (
<div>
{items.map(item => (
<div key={item.id}>{item.text}</div>
))}
<button onClick={loadMore}>Load More</button>
</div>
);
};说明:
- 图片懒加载
- 列表懒加载
- 组件懒加载
- 资源懒加载
渲染优化
重排重绘
javascript
// 批量更新
const BatchUpdate = () => {
const [items, setItems] = useState([]);
const addItems = () => {
// 使用 requestAnimationFrame 批量更新
requestAnimationFrame(() => {
const newItems = Array.from({ length: 1000 }, (_, i) => ({
id: i,
text: `Item ${i}`
}));
setItems(newItems);
});
};
return (
<div>
<button onClick={addItems}>Add Items</button>
<div style={{ height: '400px', overflow: 'auto' }}>
{items.map(item => (
<div key={item.id}>{item.text}</div>
))}
</div>
</div>
);
};
// 使用 transform 代替 top/left
const AnimatedBox = () => {
const [position, setPosition] = useState({ x: 0, y: 0 });
const move = () => {
setPosition({ x: 100, y: 100 });
};
return (
<div
style={{
transform: `translate(${position.x}px, ${position.y}px)`,
transition: 'transform 0.3s'
}}
>
Animated Box
</div>
);
};说明:
- 避免频繁重排
- 使用 transform 代替位置属性
- 批量更新 DOM
- 使用 requestAnimationFrame
虚拟列表
javascript
const VirtualList = ({ items, itemHeight, windowHeight }) => {
const [scrollTop, setScrollTop] = useState(0);
const visibleItems = items.slice(
Math.floor(scrollTop / itemHeight),
Math.ceil((scrollTop + windowHeight) / itemHeight)
);
const totalHeight = items.length * itemHeight;
const offsetY = Math.floor(scrollTop / itemHeight) * itemHeight;
return (
<div
style={{ height: windowHeight, overflow: 'auto' }}
onScroll={e => setScrollTop(e.target.scrollTop)}
>
<div style={{ height: totalHeight, position: 'relative' }}>
<div style={{ transform: `translateY(${offsetY}px)` }}>
{visibleItems.map(item => (
<div key={item.id} style={{ height: itemHeight }}>
{item.text}
</div>
))}
</div>
</div>
</div>
);
};说明:
- 只渲染可见区域
- 计算滚动位置
- 使用 transform 定位
- 保持滚动条高度
防抖节流
javascript
// 防抖
const debounce = (fn, delay) => {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
};
// 节流
const throttle = (fn, delay) => {
let last = 0;
return function (...args) {
const now = Date.now();
if (now - last > delay) {
fn.apply(this, args);
last = now;
}
};
};
// 使用示例
const SearchInput = () => {
const [value, setValue] = useState('');
const handleSearch = debounce(value => {
// 执行搜索
console.log('Searching:', value);
}, 300);
return (
<input
value={value}
onChange={e => {
setValue(e.target.value);
handleSearch(e.target.value);
}}
/>
);
};说明:
- 防抖用于输入框
- 节流用于滚动事件
- 避免频繁触发
- 提高性能
缓存优化
浏览器缓存
javascript
// 设置缓存头
app.use(express.static('public', {
maxAge: '1d',
etag: true,
lastModified: true
}));
// 使用 localStorage
const useLocalStorage = (key, initialValue) => {
const [storedValue, setStoredValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
console.error(error);
return initialValue;
}
});
const setValue = value => {
try {
const valueToStore = value instanceof Function ? value(storedValue) : value;
setStoredValue(valueToStore);
window.localStorage.setItem(key, JSON.stringify(valueToStore));
} catch (error) {
console.error(error);
}
};
return [storedValue, setValue];
};说明:
- 使用 HTTP 缓存
- 使用 localStorage
- 使用 sessionStorage
- 使用 Cookie
Service Worker
javascript
// 注册 Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js').then(registration => {
console.log('SW registered:', registration);
}).catch(error => {
console.log('SW registration failed:', error);
});
});
}
// sw.js
const CACHE_NAME = 'v1';
const urlsToCache = [
'/',
'/index.html',
'/styles.css',
'/app.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))
);
});说明:
- 离线缓存
- 资源预缓存
- 请求拦截
- 缓存策略
IndexedDB
javascript
// 打开数据库
const openDB = () => {
return new Promise((resolve, reject) => {
const request = indexedDB.open('MyDB', 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = event => {
const db = event.target.result;
db.createObjectStore('users', { keyPath: 'id' });
};
});
};
// 存储数据
const storeData = async (data) => {
const db = await openDB();
const transaction = db.transaction('users', 'readwrite');
const store = transaction.objectStore('users');
return new Promise((resolve, reject) => {
const request = store.add(data);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
};说明:
- 大容量存储
- 异步操作
- 事务支持
- 索引支持
网络优化
HTTP/2
nginx
# nginx.conf
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
location / {
root /usr/share/nginx/html;
index index.html;
}
}说明:
- 多路复用
- 头部压缩
- 服务器推送
- 二进制协议
CDN
html
<!-- 使用 CDN -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
<!-- 配置 CDN -->
<script>
window.__CDN__ = {
baseUrl: 'https://cdn.example.com',
version: '1.0.0'
};
</script>说明:
- 静态资源加速
- 全球分发
- 负载均衡
- 缓存优化
预加载
html
<!-- 预加载资源 -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="main.js" as="script">
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<!-- 预连接 -->
<link rel="preconnect" href="https://api.example.com">
<link rel="dns-prefetch" href="https://api.example.com">说明:
- 预加载关键资源
- 预连接域名
- DNS 预解析
- 资源优先级
监控分析
性能指标
javascript
// 使用 Performance API
const measurePerformance = () => {
const performance = window.performance;
const timing = performance.timing;
const metrics = {
// 页面加载时间
loadTime: timing.loadEventEnd - timing.navigationStart,
// DOM 解析时间
domReadyTime: timing.domComplete - timing.domLoading,
// 首次内容绘制
fcp: performance.getEntriesByName('first-contentful-paint')[0]?.startTime,
// 最大内容绘制
lcp: performance.getEntriesByName('largest-contentful-paint')[0]?.startTime
};
return metrics;
};
// 使用 Web Vitals
import { getCLS, getFID, getLCP } from 'web-vitals';
getCLS(console.log);
getFID(console.log);
getLCP(console.log);说明:
- 核心 Web 指标
- 性能时间线
- 资源加载时间
- 用户交互时间
错误监控
javascript
// 全局错误处理
window.onerror = (message, source, lineno, colno, error) => {
console.error('Global error:', { message, source, lineno, colno, error });
// 上报错误
};
// Promise 错误处理
window.addEventListener('unhandledrejection', event => {
console.error('Unhandled promise rejection:', event.reason);
// 上报错误
});
// 自定义错误边界
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
// 上报错误
}
render() {
if (this.state.hasError) {
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}说明:
- 全局错误捕获
- Promise 错误处理
- React 错误边界
- 错误上报
用户行为
javascript
// 用户行为跟踪
const trackUserBehavior = () => {
// 页面访问
const pageView = {
url: window.location.href,
title: document.title,
timestamp: Date.now()
};
// 点击事件
document.addEventListener('click', event => {
const click = {
target: event.target.tagName,
text: event.target.textContent,
timestamp: Date.now()
};
// 上报点击
});
// 滚动事件
let scrollTimeout;
window.addEventListener('scroll', () => {
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
const scroll = {
position: window.scrollY,
timestamp: Date.now()
};
// 上报滚动
}, 100);
});
};说明:
- 页面访问统计
- 用户点击跟踪
- 滚动行为分析
- 性能数据收集
最佳实践
1. 加载优化
- 压缩资源文件
- 使用代码分割
- 实现懒加载
- 优化图片加载
2. 渲染优化
- 减少重排重绘
- 使用虚拟列表
- 实现防抖节流
- 优化动画性能
3. 缓存优化
- 合理使用缓存
- 实现离线功能
- 优化存储策略
- 清理过期缓存
4. 网络优化
- 使用 HTTP/2
- 配置 CDN
- 实现预加载
- 优化请求策略
学习资源
在线教程
视频课程
- 慕课网
- 极客时间
- B站技术区
- YouTube 技术频道