Web 性能优化实战:从首屏加载到运行时流畅
性能优化是一个系统工程。本文从加载性能和运行时性能两个维度,全面介绍 Web 性能优化策略。
一、核心性能指标
Google Core Web Vitals
| 指标 | 含义 | 目标值 |
|---|---|---|
| LCP | 最大内容绘制时间 | < 2.5s |
| FID | 首次输入延迟 | < 100ms |
| CLS | 累积布局偏移 | < 0.1 |
其他重要指标
- FCP(First Contentful Paint)— 首次内容绘制
- TTFB(Time to First Byte)— 首字节时间
- TTI(Time to Interactive)— 可交互时间
二、加载性能优化
1. 资源压缩
// vite.config.js
export default {
build: {
minify: 'terser',
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
markdown: ['react-markdown', 'remark-gfm'],
}
}
}
}
}
2. 图片优化
图片通常是页面最大的资源,优化收益最高:
<!-- 使用 WebP 格式 -->
<picture>
<source srcset="image.webp" type="image/webp">
<source srcset="image.jpg" type="image/jpeg">
<img src="image.jpg" alt="描述" loading="lazy">
</picture>
<!-- 响应式图片 -->
<img
srcset="small.jpg 480w, medium.jpg 800w, large.jpg 1200w"
sizes="(max-width: 600px) 480px, (max-width: 900px) 800px, 1200px"
src="medium.jpg"
alt="描述"
>
3. 代码分割
// 路由级别懒加载
const BlogList = React.lazy(() => import('./pages/BlogList'))
const ArticleDetail = React.lazy(() => import('./pages/ArticleDetail'))
// 使用 Suspense 包裹
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/blog" element={<BlogList />} />
<Route path="/article/:id" element={<ArticleDetail />} />
</Routes>
</Suspense>
4. 预加载与预获取
<!-- 预加载关键资源 -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
<!-- DNS 预解析 -->
<link rel="dns-prefetch" href="//cdn.example.com">
<!-- 预连接 -->
<link rel="preconnect" href="//api.example.com">
<!-- 预获取下一页资源 -->
<link rel="prefetch" href="/next-page-data.json">
三、渲染性能优化
1. 减少重排和重绘
// ❌ 触发多次重排
element.style.width = '100px'
element.style.height = '200px'
element.style.margin = '10px'
// ✅ 使用 class 一次性修改
element.classList.add('updated')
// ✅ 使用 transform 代替 top/left(不触发重排)
.animate {
transform: translateX(100px) /* 而不是 left: 100px */
}
2. 虚拟列表
当列表数据量很大时,只渲染可见区域的元素:
import { useVirtual } from 'react-virtual'
function BigList({ items }) {
const rowVirtualizer = useVirtual({
size: items.length,
parentRef: listRef,
estimateSize: () => 60,
})
return (
<div ref={listRef} style={{ height: 600, overflow: 'auto' }}>
<div style={{ height: rowVirtualizer.totalSize }}>
{rowVirtualizer.virtualItems.map(vItem => (
<div key={vItem.index} style={{
position: 'absolute',
top: vItem.start,
height: vItem.size,
}}>
{items[vItem.index].name}
</div>
))}
</div>
</div>
)
}
3. 防抖与节流
// 防抖:延迟执行,适合搜索框
function debounce(fn, delay) {
let timer
return (...args) => {
clearTimeout(timer)
timer = setTimeout(() => fn(...args), delay)
}
}
// 节流:固定频率执行,适合滚动事件
function throttle(fn, interval) {
let lastTime = 0
return (...args) => {
const now = Date.now()
if (now - lastTime >= interval) {
fn(...args)
lastTime = now
}
}
}
// 使用
const handleSearch = debounce(search, 300)
const handleScroll = throttle(onScroll, 16) // ~60fps
四、运行时性能优化
1. React 性能
// 使用 React.memo 避免不必要重渲染
const Item = React.memo(({ data }) => {
return <div>{data.name}</div>
})
// 使用 useMemo 缓存计算结果
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
)
// 使用 useCallback 缓存函数
const handleClick = useCallback((id) => {
setSelected(id)
}, [])
2. Web Worker 处理密集计算
// main.js
const worker = new Worker('worker.js')
worker.postMessage({ data: largeArray })
worker.onmessage = (e) => {
console.log('处理结果:', e.data)
}
// worker.js
self.onmessage = (e) => {
const result = heavyComputation(e.data.data)
self.postMessage(result)
}
3. requestAnimationFrame
// 动画使用 rAF 而不是 setInterval
function animate() {
// 更新动画
requestAnimationFrame(animate)
}
requestAnimationFrame(animate)
五、监控与分析
Performance Observer
// 监控 LCP
new PerformanceObserver((list) => {
const entries = list.getEntries()
const lastEntry = entries[entries.length - 1]
console.log('LCP:', lastEntry.startTime)
}).observe({ type: 'largest-contentful-paint', buffered: true })
// 监控 CLS
new PerformanceObserver((list) => {
let cls = 0
for (const entry of list.getEntries()) {
cls += entry.value
}
console.log('CLS:', cls)
}).observe({ type: 'layout-shift', buffered: true })
总结
性能优化是一个持续的过程:
- 先测量 — 使用 Lighthouse、Chrome DevTools 分析瓶颈
- 定优先级 — 先优化影响最大的问题
- 逐步优化 — 每次优化后重新测量
- 持续监控 — 建立性能监控告警
记住:过早优化是万恶之源。先写出正确的代码,再在需要时优化。
📖 相关阅读
🔗
前端开发
2024-12-15
React 开发最佳实践指南(2024 版)
从组件设计到状态管理,从性能优化到代码规范,全面总结 React 开发中的最佳实践。
#React#前端#最佳实践
12 分钟阅读
阅读全文 →
⚙️
前端开发
2024-11-10
深入理解 JavaScript 异步编程
从回调到 Promise 再到 async/await,彻底搞懂 JavaScript 异步编程的演进与最佳实践。
#JavaScript#异步编程#前端
15 分钟阅读
阅读全文 →
📘
前端开发
2024-09-01
TypeScript 泛型完全指南
从基础到高级,彻底搞懂 TypeScript 泛型,写出更灵活、更类型安全的代码。
#TypeScript#前端#类型系统
12 分钟阅读
阅读全文 →