性能优化总览
前端性能优化的常用手段和实践方案
问题
前端性能优化有哪些常用手段?
解答
1. 加载优化
资源压缩与合并
// webpack 配置示例
module.exports = {
optimization: {
minimize: true,
splitChunks: {
chunks: 'all',
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
name: 'vendors'
}
}
}
}
}
图片优化
<!-- 响应式图片 -->
<img
srcset="small.jpg 300w, medium.jpg 600w, large.jpg 900w"
sizes="(max-width: 320px) 280px, (max-width: 640px) 580px, 800px"
src="medium.jpg"
alt="响应式图片"
/>
<!-- 懒加载 -->
<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" />
代码分割与懒加载
// React 懒加载
const LazyComponent = React.lazy(() => import('./HeavyComponent'))
function App() {
return (
<Suspense fallback={<Loading />}>
<LazyComponent />
</Suspense>
)
}
// Vue 路由懒加载
const routes = [
{
path: '/dashboard',
component: () => import('./views/Dashboard.vue')
}
]
2. 渲染优化
避免强制同步布局
// 错误:读写交替,触发多次重排
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' // 批量写
})
使用 requestAnimationFrame
// 动画使用 rAF
function animate() {
element.style.transform = `translateX(${position}px)`
position += 1
if (position < 300) {
requestAnimationFrame(animate)
}
}
requestAnimationFrame(animate)
虚拟列表
// 简化版虚拟列表
function VirtualList({ items, itemHeight, containerHeight }) {
const [scrollTop, setScrollTop] = useState(0)
// 计算可见范围
const startIndex = Math.floor(scrollTop / itemHeight)
const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / itemHeight) + 1,
items.length
)
// 只渲染可见项
const visibleItems = items.slice(startIndex, endIndex)
const offsetY = startIndex * itemHeight
return (
<div
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={e => setScrollTop(e.target.scrollTop)}
>
<div style={{ height: items.length * itemHeight }}>
<div style={{ transform: `translateY(${offsetY}px)` }}>
{visibleItems.map((item, i) => (
<div key={startIndex + i} style={{ height: itemHeight }}>
{item}
</div>
))}
</div>
</div>
</div>
)
}
3. 网络优化
缓存策略
// Service Worker 缓存
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(cached => {
// 缓存优先,同时更新缓存
const fetched = fetch(event.request).then(response => {
const clone = response.clone()
caches.open('v1').then(cache => cache.put(event.request, clone))
return response
})
return cached || fetched
})
)
})
HTTP 缓存头
# Nginx 配置
location ~* \.(js|css|png|jpg|gif|ico)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location ~* \.html$ {
expires -1;
add_header Cache-Control "no-cache, must-revalidate";
}
预加载关键资源
<!-- 预加载 -->
<link rel="preload" href="critical.css" as="style" />
<link rel="preload" href="main.js" as="script" />
<!-- 预连接 -->
<link rel="preconnect" href="https://api.example.com" />
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="https://cdn.example.com" />
4. 代码优化
防抖与节流
// 防抖:停止触发后执行
function debounce(fn, delay) {
let timer = null
return function(...args) {
clearTimeout(timer)
timer = setTimeout(() => fn.apply(this, args), delay)
}
}
// 节流:固定间隔执行
function throttle(fn, interval) {
let lastTime = 0
return function(...args) {
const now = Date.now()
if (now - lastTime >= interval) {
lastTime = now
fn.apply(this, args)
}
}
}
// 使用
input.addEventListener('input', debounce(search, 300))
window.addEventListener('scroll', throttle(handleScroll, 100))
Web Worker 处理耗时任务
// worker.js
self.onmessage = function(e) {
const result = heavyComputation(e.data)
self.postMessage(result)
}
// main.js
const worker = new Worker('worker.js')
worker.postMessage(largeData)
worker.onmessage = function(e) {
console.log('计算结果:', e.data)
}
5. 性能监控
// 使用 Performance API
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.domInteractive - timing.responseEnd,
// 页面完全加载
load: timing.loadEventEnd - timing.navigationStart
}
// 监控 LCP、FID、CLS
new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
console.log('LCP:', entry.startTime)
}
}).observe({ entryTypes: ['largest-contentful-paint'] })
关键点
- 加载优化:压缩资源、代码分割、懒加载、使用 CDN
- 渲染优化:减少重排重绘、使用 rAF、虚拟列表处理大数据
- 缓存策略:合理设置 HTTP 缓存、使用 Service Worker
- 代码层面:防抖节流、Web Worker、避免内存泄漏
- 持续监控:使用 Performance API 和 Web Vitals 指标(LCP、FID、CLS)
目录